Repository: eclipse-ee4j/yasson Branch: main Commit: ba42fac2dfcc Files: 596 Total size: 1.6 MB Directory structure: gitextract_u4tfpvr0/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── other-issue.md │ ├── dependabot.yml │ └── workflows/ │ └── maven.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── NOTICE.md ├── README.md ├── etc/ │ ├── checkstyle-suppressions.xml │ ├── checkstyle.xml │ ├── copyright-exclude.txt │ ├── copyright.sh │ ├── copyright.txt │ └── delivery-checks.sh ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── module-info.java │ │ │ └── org/ │ │ │ └── eclipse/ │ │ │ └── yasson/ │ │ │ ├── FieldAccessStrategy.java │ │ │ ├── ImplementationClass.java │ │ │ ├── JsonBindingProvider.java │ │ │ ├── YassonConfig.java │ │ │ ├── YassonJsonb.java │ │ │ ├── YassonProperties.java │ │ │ ├── internal/ │ │ │ │ ├── AnnotationFinder.java │ │ │ │ ├── AnnotationIntrospector.java │ │ │ │ ├── BuiltInTypes.java │ │ │ │ ├── ClassMultiReleaseExtension.java │ │ │ │ ├── ClassParser.java │ │ │ │ ├── ComponentMatcher.java │ │ │ │ ├── ConstructorPropertiesAnnotationIntrospector.java │ │ │ │ ├── DeserializationContextImpl.java │ │ │ │ ├── InstanceCreator.java │ │ │ │ ├── JsonBinding.java │ │ │ │ ├── JsonBindingBuilder.java │ │ │ │ ├── JsonbConfigProperties.java │ │ │ │ ├── JsonbContext.java │ │ │ │ ├── JsonbDateFormatter.java │ │ │ │ ├── JsonbNumberFormatter.java │ │ │ │ ├── MappingContext.java │ │ │ │ ├── ProcessingContext.java │ │ │ │ ├── ReflectionUtils.java │ │ │ │ ├── ResolvedParameterizedType.java │ │ │ │ ├── SerializationContextImpl.java │ │ │ │ ├── VariableTypeInheritanceSearch.java │ │ │ │ ├── components/ │ │ │ │ │ ├── AbstractComponentBinding.java │ │ │ │ │ ├── AdapterBinding.java │ │ │ │ │ ├── BeanManagerInstanceCreator.java │ │ │ │ │ ├── ComponentBindings.java │ │ │ │ │ ├── DefaultConstructorCreator.java │ │ │ │ │ ├── DeserializerBinding.java │ │ │ │ │ ├── JsonbComponentInstanceCreatorFactory.java │ │ │ │ │ └── SerializerBinding.java │ │ │ │ ├── deserializer/ │ │ │ │ │ ├── AdapterDeserializer.java │ │ │ │ │ ├── ArrayDeserializer.java │ │ │ │ │ ├── ArrayInstanceCreator.java │ │ │ │ │ ├── CollectionDeserializer.java │ │ │ │ │ ├── CollectionInstanceCreator.java │ │ │ │ │ ├── ContextSwitcher.java │ │ │ │ │ ├── CyclicReferenceDeserializer.java │ │ │ │ │ ├── DefaultObjectInstanceCreator.java │ │ │ │ │ ├── DeferredDeserializer.java │ │ │ │ │ ├── DeserializationModelCreator.java │ │ │ │ │ ├── InheritanceInstanceCreator.java │ │ │ │ │ ├── JsonbCreatorDeserializer.java │ │ │ │ │ ├── JustReturn.java │ │ │ │ │ ├── MapDeserializer.java │ │ │ │ │ ├── MapInstanceCreator.java │ │ │ │ │ ├── ModelDeserializer.java │ │ │ │ │ ├── NullCheckDeserializer.java │ │ │ │ │ ├── ObjectDeserializer.java │ │ │ │ │ ├── OptionalDeserializer.java │ │ │ │ │ ├── PositionChecker.java │ │ │ │ │ ├── RequiredCreatorParameter.java │ │ │ │ │ ├── UserDefinedDeserializer.java │ │ │ │ │ ├── ValueExtractor.java │ │ │ │ │ ├── ValueSetterDeserializer.java │ │ │ │ │ ├── YassonParser.java │ │ │ │ │ └── types/ │ │ │ │ │ ├── AbstractDateDeserializer.java │ │ │ │ │ ├── AbstractNumberDeserializer.java │ │ │ │ │ ├── BigDecimalDeserializer.java │ │ │ │ │ ├── BigIntegerDeserializer.java │ │ │ │ │ ├── BooleanDeserializer.java │ │ │ │ │ ├── ByteDeserializer.java │ │ │ │ │ ├── CalendarDeserializer.java │ │ │ │ │ ├── CharDeserializer.java │ │ │ │ │ ├── DateDeserializer.java │ │ │ │ │ ├── DoubleDeserializer.java │ │ │ │ │ ├── DurationDeserializer.java │ │ │ │ │ ├── EnumDeserializer.java │ │ │ │ │ ├── FloatDeserializer.java │ │ │ │ │ ├── InstantDeserializer.java │ │ │ │ │ ├── IntegerDeserializer.java │ │ │ │ │ ├── JsonValueDeserializer.java │ │ │ │ │ ├── LocalDateDeserializer.java │ │ │ │ │ ├── LocalDateTimeDeserializer.java │ │ │ │ │ ├── LocalTimeDeserializer.java │ │ │ │ │ ├── LongDeserializer.java │ │ │ │ │ ├── MonthDayTypeDeserializer.java │ │ │ │ │ ├── NumberDeserializer.java │ │ │ │ │ ├── ObjectTypeDeserializer.java │ │ │ │ │ ├── OffsetDateTimeDeserializer.java │ │ │ │ │ ├── OffsetTimeDeserializer.java │ │ │ │ │ ├── OptionalDoubleDeserializer.java │ │ │ │ │ ├── OptionalIntDeserializer.java │ │ │ │ │ ├── OptionalLongDeserializer.java │ │ │ │ │ ├── PathDeserializer.java │ │ │ │ │ ├── PeriodDeserializer.java │ │ │ │ │ ├── ShortDeserializer.java │ │ │ │ │ ├── SqlDateDeserializer.java │ │ │ │ │ ├── SqlTimestampDeserializer.java │ │ │ │ │ ├── StringDeserializer.java │ │ │ │ │ ├── TimeZoneDeserializer.java │ │ │ │ │ ├── TypeDeserializer.java │ │ │ │ │ ├── TypeDeserializerBuilder.java │ │ │ │ │ ├── TypeDeserializers.java │ │ │ │ │ ├── UriDeserializer.java │ │ │ │ │ ├── UrlDeserializer.java │ │ │ │ │ ├── UuidDeserializer.java │ │ │ │ │ ├── XmlGregorianCalendarDeserializer.java │ │ │ │ │ ├── YearMonthTypeDeserializer.java │ │ │ │ │ ├── ZoneIdDeserializer.java │ │ │ │ │ ├── ZoneOffsetDeserializer.java │ │ │ │ │ └── ZonedDateTimeDeserializer.java │ │ │ │ ├── jsonstructure/ │ │ │ │ │ ├── JsonArrayBuilder.java │ │ │ │ │ ├── JsonArrayIterator.java │ │ │ │ │ ├── JsonGeneratorToStructureAdapter.java │ │ │ │ │ ├── JsonObjectBuilder.java │ │ │ │ │ ├── JsonObjectIterator.java │ │ │ │ │ ├── JsonStructureBuilder.java │ │ │ │ │ ├── JsonStructureIterator.java │ │ │ │ │ └── JsonStructureToParserAdapter.java │ │ │ │ ├── model/ │ │ │ │ │ ├── AnnotationTarget.java │ │ │ │ │ ├── ClassModel.java │ │ │ │ │ ├── CreatorModel.java │ │ │ │ │ ├── JsonbAnnotatedElement.java │ │ │ │ │ ├── JsonbCreator.java │ │ │ │ │ ├── ModulesUtil.java │ │ │ │ │ ├── Property.java │ │ │ │ │ ├── PropertyModel.java │ │ │ │ │ ├── ReverseTreeMap.java │ │ │ │ │ └── customization/ │ │ │ │ │ ├── ClassCustomization.java │ │ │ │ │ ├── ComponentBoundCustomization.java │ │ │ │ │ ├── CreatorCustomization.java │ │ │ │ │ ├── Customization.java │ │ │ │ │ ├── CustomizationBase.java │ │ │ │ │ ├── PropertyCustomization.java │ │ │ │ │ ├── PropertyOrdering.java │ │ │ │ │ ├── StrategiesProvider.java │ │ │ │ │ ├── TypeInheritanceConfiguration.java │ │ │ │ │ └── VisibilityStrategiesProvider.java │ │ │ │ ├── properties/ │ │ │ │ │ ├── MessageKeys.java │ │ │ │ │ └── Messages.java │ │ │ │ └── serializer/ │ │ │ │ ├── AbstractSerializer.java │ │ │ │ ├── AdapterSerializer.java │ │ │ │ ├── ArraySerializer.java │ │ │ │ ├── CollectionSerializer.java │ │ │ │ ├── CyclicReferenceSerializer.java │ │ │ │ ├── KeyWriter.java │ │ │ │ ├── MapSerializer.java │ │ │ │ ├── ModelSerializer.java │ │ │ │ ├── NullSerializer.java │ │ │ │ ├── NullVisibilitySwitcher.java │ │ │ │ ├── ObjectSerializer.java │ │ │ │ ├── OptionalSerializer.java │ │ │ │ ├── RecursionChecker.java │ │ │ │ ├── SerializationModelCreator.java │ │ │ │ ├── SerializerBuilderParams.java │ │ │ │ ├── UserDefinedSerializer.java │ │ │ │ ├── ValueGetterSerializer.java │ │ │ │ ├── YassonGenerator.java │ │ │ │ └── types/ │ │ │ │ ├── AbstractDateSerializer.java │ │ │ │ ├── AbstractNumberSerializer.java │ │ │ │ ├── BigDecimalSerializer.java │ │ │ │ ├── BigIntegerSerializer.java │ │ │ │ ├── BooleanSerializer.java │ │ │ │ ├── ByteSerializer.java │ │ │ │ ├── CalendarSerializer.java │ │ │ │ ├── CharSerializer.java │ │ │ │ ├── DateSerializer.java │ │ │ │ ├── DoubleSerializer.java │ │ │ │ ├── DurationSerializer.java │ │ │ │ ├── EnumSerializer.java │ │ │ │ ├── FloatSerializer.java │ │ │ │ ├── InstantSerializer.java │ │ │ │ ├── IntegerSerializer.java │ │ │ │ ├── JsonValueSerializer.java │ │ │ │ ├── LocalDateSerializer.java │ │ │ │ ├── LocalDateTimeSerializer.java │ │ │ │ ├── LocalTimeSerializer.java │ │ │ │ ├── LongSerializer.java │ │ │ │ ├── MonthDayTypeSerializer.java │ │ │ │ ├── NumberSerializer.java │ │ │ │ ├── ObjectTypeSerializer.java │ │ │ │ ├── OffsetDateTimeSerializer.java │ │ │ │ ├── OffsetTimeSerializer.java │ │ │ │ ├── OptionalDoubleSerializer.java │ │ │ │ ├── OptionalIntSerializer.java │ │ │ │ ├── OptionalLongSerializer.java │ │ │ │ ├── PathSerializer.java │ │ │ │ ├── PeriodSerializer.java │ │ │ │ ├── ShortSerializer.java │ │ │ │ ├── SqlDateSerializer.java │ │ │ │ ├── SqlTimestampSerializer.java │ │ │ │ ├── StringSerializer.java │ │ │ │ ├── TimeZoneSerializer.java │ │ │ │ ├── TypeSerializer.java │ │ │ │ ├── TypeSerializerBuilder.java │ │ │ │ ├── TypeSerializers.java │ │ │ │ ├── UriSerializer.java │ │ │ │ ├── UrlSerializer.java │ │ │ │ ├── UuidSerializer.java │ │ │ │ ├── XmlGregorianCalendarSerializer.java │ │ │ │ ├── YearMonthTypeSerializer.java │ │ │ │ ├── ZoneIdSerializer.java │ │ │ │ ├── ZoneOffsetSerializer.java │ │ │ │ └── ZonedDateTimeSerializer.java │ │ │ └── spi/ │ │ │ └── JsonbComponentInstanceCreator.java │ │ ├── java16/ │ │ │ └── org/ │ │ │ └── eclipse/ │ │ │ └── yasson/ │ │ │ └── internal/ │ │ │ └── ClassMultiReleaseExtension.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── native-image/ │ │ │ │ └── org.eclipse/ │ │ │ │ └── yasson/ │ │ │ │ └── native-image.properties │ │ │ └── services/ │ │ │ └── jakarta.json.bind.spi.JsonbProvider │ │ └── yasson-messages.properties │ └── test/ │ ├── java/ │ │ ├── PackagelessClassTest.java │ │ ├── PackagelessModel.java │ │ └── org/ │ │ └── eclipse/ │ │ └── yasson/ │ │ ├── Assertions.java │ │ ├── DefaultGetterInInterface.java │ │ ├── FieldAccessStrategyTest.java │ │ ├── Issue454Test.java │ │ ├── Issue456Test.java │ │ ├── JavaxNamingExcludedTest.java │ │ ├── Jsonbs.java │ │ ├── SimpleTest.java │ │ ├── TestTypeToken.java │ │ ├── YassonConfigTest.java │ │ ├── adapters/ │ │ │ ├── AdaptersTest.java │ │ │ ├── JsonbTypeAdapterTest.java │ │ │ └── model/ │ │ │ ├── AdaptedPojo.java │ │ │ ├── Author.java │ │ │ ├── Box.java │ │ │ ├── BoxToCrateCompatibleGenericsAdapter.java │ │ │ ├── BoxToCratePropagatedIntegerStringAdapter.java │ │ │ ├── BoxToCratePropagatedTypeArgsAdapter.java │ │ │ ├── BoxToJsonObjectAdapter.java │ │ │ ├── BoxWithAdapter.java │ │ │ ├── BoxWithAdapterAdapter.java │ │ │ ├── BoxWithDeserializer.java │ │ │ ├── BoxWithDeserializerDeserializer.java │ │ │ ├── BoxWithSerializer.java │ │ │ ├── BoxWithSerializerSerializer.java │ │ │ ├── Chain.java │ │ │ ├── ChainAdapter.java │ │ │ ├── ChainSerializer.java │ │ │ ├── Crate.java │ │ │ ├── FirstNameAdapter.java │ │ │ ├── Foo.java │ │ │ ├── FooAdapter.java │ │ │ ├── FooSerializer.java │ │ │ ├── GenericBox.java │ │ │ ├── GenericCrate.java │ │ │ ├── IntegerListToStringAdapter.java │ │ │ ├── JsonObjectPojo.java │ │ │ ├── LocalPolymorphicAdapter.java │ │ │ ├── LocalTypeWrapper.java │ │ │ ├── MultiinterfaceAdapter.java │ │ │ ├── MultilevelAdapterClass.java │ │ │ ├── NumberAdapter.java │ │ │ ├── ReturnNullAdapter.java │ │ │ ├── SerializableAdapter.java │ │ │ ├── SupertypeAdapterPojo.java │ │ │ ├── UUIDContainer.java │ │ │ ├── UUIDMapperClsBased.java │ │ │ └── UUIDMapperIfcBased.java │ │ ├── customization/ │ │ │ ├── AnnotationInheritanceTest.java │ │ │ ├── EncodingTest.java │ │ │ ├── ImplementationClassTest.java │ │ │ ├── InterfaceAnnotationsTest.java │ │ │ ├── JsonbCreatorTest.java │ │ │ ├── JsonbDateFormatterTest.java │ │ │ ├── JsonbNillableTest.java │ │ │ ├── JsonbPropertyTest.java │ │ │ ├── JsonbPropertyVisibilityStrategyTest.java │ │ │ ├── NumberFormatTest.java │ │ │ ├── PrettyPrintTest.java │ │ │ ├── PropertyOrderTest.java │ │ │ ├── YassonSpecificConfigTests.java │ │ │ ├── model/ │ │ │ │ ├── Animal.java │ │ │ │ ├── CollectionsWithFormatters.java │ │ │ │ ├── CreatorConstructorPojo.java │ │ │ │ ├── CreatorFactoryMethodPojo.java │ │ │ │ ├── CreatorIncompatibleTypePojo.java │ │ │ │ ├── CreatorMultipleDeclarationErrorPojo.java │ │ │ │ ├── CreatorPackagePrivateConstructor.java │ │ │ │ ├── CreatorWithoutJavabeanProperty.java │ │ │ │ ├── CreatorWithoutJsonbProperty.java │ │ │ │ ├── CreatorWithoutJsonbProperty1.java │ │ │ │ ├── DateFormatPojo.java │ │ │ │ ├── DateFormatPojoWithClassLevelFormatter.java │ │ │ │ ├── Dog.java │ │ │ │ ├── FieldCustomOrder.java │ │ │ │ ├── FieldCustomOrderWrapper.java │ │ │ │ ├── FieldOrder.java │ │ │ │ ├── FieldOrderNameAnnotation.java │ │ │ │ ├── FieldSpecificOrder.java │ │ │ │ ├── ImplementationClassPojo.java │ │ │ │ ├── InheritedAnnotationsPojo.java │ │ │ │ ├── InheritsJsonbProperty.java │ │ │ │ ├── InheritsNillable.java │ │ │ │ ├── InheritsNillableRecursion.java │ │ │ │ ├── InterfacedPojoA.java │ │ │ │ ├── InterfacedPojoAbsImpl.java │ │ │ │ ├── InterfacedPojoB.java │ │ │ │ ├── InterfacedPojoC.java │ │ │ │ ├── InterfacedPojoImpl.java │ │ │ │ ├── JsonbNillableClassFirstLevel.java │ │ │ │ ├── JsonbNillableClassSecondLevel.java │ │ │ │ ├── JsonbNillableInterfaceBase.java │ │ │ │ ├── JsonbNillableInterfaceBaseOne.java │ │ │ │ ├── JsonbNillableInterfaceBaseTwo.java │ │ │ │ ├── JsonbNillableOverriddenWithJsonbProperty.java │ │ │ │ ├── JsonbNillableOverridesClass.java │ │ │ │ ├── JsonbNillableOverridesInterface.java │ │ │ │ ├── JsonbNillableValue.java │ │ │ │ ├── JsonbPropertyName.java │ │ │ │ ├── JsonbPropertyNameCollision.java │ │ │ │ ├── JsonbPropertyNillable.java │ │ │ │ ├── NumberFormatPojo.java │ │ │ │ ├── NumberFormatPojoWithoutClassLevelFormatter.java │ │ │ │ ├── ParameterNameTester.java │ │ │ │ ├── RenamedPropertiesContainer.java │ │ │ │ ├── TrimmedDatePojo.java │ │ │ │ └── packagelevelannotations/ │ │ │ │ ├── JsonbNillablePackageLevel.java │ │ │ │ ├── PackageLevelOverriddenWithClassLevel.java │ │ │ │ └── package-info.java │ │ │ ├── polymorphism/ │ │ │ │ ├── AnnotationPolymorphismTest.java │ │ │ │ ├── MultiplePolymorphicInfoTest.java │ │ │ │ └── NestedPolymorphismTest.java │ │ │ └── transients/ │ │ │ ├── JsonbTransientTest.java │ │ │ └── models/ │ │ │ ├── JsonbTransientCollisionOnGetter.java │ │ │ ├── JsonbTransientCollisionOnProperty.java │ │ │ ├── JsonbTransientCollisionOnPropertyAndGetter.java │ │ │ ├── JsonbTransientCollisionOnPropertyAndGetterAndSetter.java │ │ │ ├── JsonbTransientCollisionOnPropertyAndSetter.java │ │ │ ├── JsonbTransientCollisionOnSetter.java │ │ │ ├── JsonbTransientValue.java │ │ │ ├── TransientGetterNoField.java │ │ │ ├── TransientGetterPlusCustomizationAnnotatedFieldContainer.java │ │ │ ├── TransientSetterPlusCustomizationAnnotatedFieldContainer.java │ │ │ └── TransientSetterPlusCustomizationAnnotatedGetterContainer.java │ │ ├── defaultmapping/ │ │ │ ├── EnumTest.java │ │ │ ├── IJsonTest.java │ │ │ ├── anonymous/ │ │ │ │ ├── AnonymousClassTest.java │ │ │ │ └── OuterPojo.java │ │ │ ├── basic/ │ │ │ │ ├── BasicTest.java │ │ │ │ ├── BooleanTest.java │ │ │ │ ├── NumberTest.java │ │ │ │ ├── PropertyMismatchTest.java │ │ │ │ ├── SingleValueTest.java │ │ │ │ ├── UnqualifiedPropertiesTest.java │ │ │ │ └── model/ │ │ │ │ ├── BigDecimalInNumber.java │ │ │ │ └── BooleanModel.java │ │ │ ├── collections/ │ │ │ │ ├── ArrayTest.java │ │ │ │ ├── CollectionsTest.java │ │ │ │ ├── Language.java │ │ │ │ └── MapKeyTypesTest.java │ │ │ ├── dates/ │ │ │ │ ├── DatesTest.java │ │ │ │ └── model/ │ │ │ │ ├── AbstractDateTimePojo.java │ │ │ │ ├── CalendarPojo.java │ │ │ │ ├── ClassLevelDateAnnotation.java │ │ │ │ ├── ClassLevelDateAnnotationParent.java │ │ │ │ ├── CollectionDatePojo.java │ │ │ │ ├── DatePojo.java │ │ │ │ ├── DateWithZonePojo.java │ │ │ │ ├── InstantPojo.java │ │ │ │ ├── LocalDatePojo.java │ │ │ │ ├── LocalDateTimePojo.java │ │ │ │ ├── LocalTimePojo.java │ │ │ │ ├── MonthDayPojo.java │ │ │ │ ├── OffsetDateTimePojo.java │ │ │ │ ├── OffsetTimePojo.java │ │ │ │ ├── YearMonthPojo.java │ │ │ │ └── ZonedDateTimePojo.java │ │ │ ├── generics/ │ │ │ │ ├── GenericsTest.java │ │ │ │ └── model/ │ │ │ │ ├── AbstractGenericWrapper.java │ │ │ │ ├── AbstractMember.java │ │ │ │ ├── AnotherGenericTestClass.java │ │ │ │ ├── BoundedGenericClass.java │ │ │ │ ├── Circle.java │ │ │ │ ├── CollectionContainer.java │ │ │ │ ├── CollectionElement.java │ │ │ │ ├── CollectionWrapper.java │ │ │ │ ├── ColoredCircle.java │ │ │ │ ├── ConstructorContainer.java │ │ │ │ ├── CyclicSubClass.java │ │ │ │ ├── ExtendedGenericTestClass.java │ │ │ │ ├── FinalGenericWrapper.java │ │ │ │ ├── FinalMember.java │ │ │ │ ├── GenericArrayClass.java │ │ │ │ ├── GenericTestClass.java │ │ │ │ ├── GenericWithUnboundedWildcardClass.java │ │ │ │ ├── LowerBoundTypeVariableWithCollectionAttributeClass.java │ │ │ │ ├── MiddleGenericWrapper.java │ │ │ │ ├── MultiLevelExtendedGenericTestClass.java │ │ │ │ ├── MultipleBoundsContainer.java │ │ │ │ ├── MyCyclicGenericClass.java │ │ │ │ ├── PropagatedGenericClass.java │ │ │ │ ├── ScalarValueWrapper.java │ │ │ │ ├── Shape.java │ │ │ │ ├── StaticCreatorContainer.java │ │ │ │ ├── TreeContainer.java │ │ │ │ ├── TreeElement.java │ │ │ │ ├── TreeTypeContainer.java │ │ │ │ ├── TypeContainer.java │ │ │ │ ├── WildCardClass.java │ │ │ │ └── WildcardMultipleBoundsClass.java │ │ │ ├── inheritance/ │ │ │ │ ├── InheritanceTest.java │ │ │ │ └── model/ │ │ │ │ ├── AbstractZeroLevel.java │ │ │ │ ├── FirstLevel.java │ │ │ │ ├── PartialOverride.java │ │ │ │ ├── PartialOverrideBase.java │ │ │ │ ├── PropertyOrderFirst.java │ │ │ │ ├── PropertyOrderSecond.java │ │ │ │ ├── PropertyOrderZero.java │ │ │ │ ├── SecondLevel.java │ │ │ │ └── generics/ │ │ │ │ ├── AbstractZeroLevelGeneric.java │ │ │ │ ├── AnotherGenericInterface.java │ │ │ │ ├── ExtendsExtendsPropagatedGenericClass.java │ │ │ │ ├── ExtendsPropagatedGenericClass.java │ │ │ │ ├── FirstLevelGeneric.java │ │ │ │ ├── GenericInterface.java │ │ │ │ ├── ImplementsGenericInterfaces.java │ │ │ │ └── SecondLevelGeneric.java │ │ │ ├── jsonp/ │ │ │ │ ├── JsonpLong.java │ │ │ │ ├── JsonpString.java │ │ │ │ ├── JsonpTest.java │ │ │ │ └── model/ │ │ │ │ └── JsonpPojo.java │ │ │ ├── lambda/ │ │ │ │ ├── Addressable.java │ │ │ │ ├── Cat.java │ │ │ │ ├── LambdaExpressionTest.java │ │ │ │ ├── Pet.java │ │ │ │ └── Robot.java │ │ │ ├── modifiers/ │ │ │ │ ├── ClassModifiersTest.java │ │ │ │ ├── DefaultMappingModifiersTest.java │ │ │ │ └── model/ │ │ │ │ ├── ChildOfPackagePrivateParent.java │ │ │ │ ├── FieldModifiersClass.java │ │ │ │ ├── MethodModifiersClass.java │ │ │ │ ├── PackagePrivateParent.java │ │ │ │ ├── Person.java │ │ │ │ ├── PrivateConstructorClass.java │ │ │ │ └── ProtectedConstructorClass.java │ │ │ ├── properties/ │ │ │ │ └── PropertiesTest.java │ │ │ ├── specific/ │ │ │ │ ├── CustomerTest.java │ │ │ │ ├── JsonStreamsTest.java │ │ │ │ ├── NullTest.java │ │ │ │ ├── ObjectGraphTest.java │ │ │ │ ├── OptionalTest.java │ │ │ │ ├── RecursiveReferenceTest.java │ │ │ │ ├── SpecificTest.java │ │ │ │ ├── UnmarshallingUnsupportedTypesTest.java │ │ │ │ └── model/ │ │ │ │ ├── Address.java │ │ │ │ ├── ClassWithUnsupportedFields.java │ │ │ │ ├── CustomUnsupportedInterface.java │ │ │ │ ├── Customer.java │ │ │ │ ├── NotMatchingGettersAndSetters.java │ │ │ │ ├── OptionalWrapper.java │ │ │ │ ├── SpecificOptionalWrapper.java │ │ │ │ ├── Street.java │ │ │ │ ├── StreetWithPrimitives.java │ │ │ │ └── SupportedTypes.java │ │ │ └── typeConvertors/ │ │ │ ├── DefaultSerializersTest.java │ │ │ └── model/ │ │ │ ├── BigDecimalWrapper.java │ │ │ ├── BigIntegerWrapper.java │ │ │ ├── ByteArrayWrapper.java │ │ │ ├── CalendarWrapper.java │ │ │ └── StringWrapper.java │ │ ├── documented/ │ │ │ └── DocumentationExampleTest.java │ │ ├── internal/ │ │ │ ├── AnnotationFinderTest.java │ │ │ ├── AnnotationFinderTestFixtures.java │ │ │ ├── AnnotationIntrospectorTest.java │ │ │ ├── AnnotationIntrospectorTestAsserts.java │ │ │ ├── AnnotationIntrospectorTestFixtures.java │ │ │ ├── AnnotationIntrospectorWithoutOptionalModulesTest.java │ │ │ ├── ClassParserTest.java │ │ │ ├── CollectionsWithJavaBaseTypesTest.java │ │ │ ├── ConstructorPropertiesAnnotationIntrospectorTest.java │ │ │ ├── JsonBindingTest.java │ │ │ ├── ReflectionUtilsTest.java │ │ │ ├── cdi/ │ │ │ │ ├── AdaptedPojo.java │ │ │ │ ├── CalledMethods.java │ │ │ │ ├── CdiDependentAdapter.java │ │ │ │ ├── CdiInjectionTest.java │ │ │ │ ├── CdiTestService.java │ │ │ │ ├── Hello1.java │ │ │ │ ├── Hello2.java │ │ │ │ ├── HelloService1.java │ │ │ │ ├── HelloService2.java │ │ │ │ ├── IHelloService.java │ │ │ │ ├── JndiBeanManager.java │ │ │ │ ├── MethodCalledEvent.java │ │ │ │ ├── MockInjectionTarget.java │ │ │ │ ├── MockInjectionTargetFactory.java │ │ │ │ ├── MockJndiContext.java │ │ │ │ ├── MockJndiContextFactory.java │ │ │ │ ├── NonCdiAdapter.java │ │ │ │ └── WeldManager.java │ │ │ ├── concurrent/ │ │ │ │ ├── JsonProcessingResult.java │ │ │ │ ├── MarshallerTask.java │ │ │ │ ├── MarshallerTaskResult.java │ │ │ │ ├── MultiTenancyTest.java │ │ │ │ ├── ResultChecker.java │ │ │ │ └── UnmarshallerTask.java │ │ │ ├── model/ │ │ │ │ ├── ModulesUtil.java │ │ │ │ └── customization/ │ │ │ │ └── naming/ │ │ │ │ ├── NamingPojo.java │ │ │ │ └── PropertyNamingStrategyTest.java │ │ │ └── serializer/ │ │ │ └── ObjectDeserializerTest.java │ │ ├── jsonpsubstitution/ │ │ │ ├── AdaptedJsonParser.java │ │ │ ├── PreinstantiatedJsonpTest.java │ │ │ └── SuffixJsonGenerator.java │ │ ├── jsonstructure/ │ │ │ ├── InnerPojo.java │ │ │ ├── InnerPojoDeserializer.java │ │ │ ├── InnerPojoSerializer.java │ │ │ ├── Issue673.java │ │ │ ├── JsonGeneratorToStructureAdapterTest.java │ │ │ ├── JsonStructureToParserAdapterTest.java │ │ │ └── Pojo.java │ │ ├── logger/ │ │ │ └── JsonbLoggerFormatter.java │ │ ├── records/ │ │ │ ├── Car.java │ │ │ ├── CarWithCreateNamingStrategyTest.java │ │ │ ├── CarWithCreator.java │ │ │ ├── CarWithDefaultConstructor.java │ │ │ ├── CarWithExtraMethod.java │ │ │ ├── CarWithGenerics.java │ │ │ ├── CarWithMultipleConstructors.java │ │ │ ├── CarWithMultipleConstructorsAndCreator.java │ │ │ ├── CarWithoutAnnotations.java │ │ │ ├── Color.java │ │ │ └── RecordTest.java │ │ └── serializers/ │ │ ├── MapToEntriesArraySerializerTest.java │ │ ├── MapToObjectSerializerTest.java │ │ ├── SerializersTest.java │ │ ├── TypeDeserializerOnContainersTest.java │ │ ├── TypeSerializerOnContainersTest.java │ │ └── model/ │ │ ├── AbstractJsonbSerializer.java │ │ ├── AnnotatedGenericWithSerializerType.java │ │ ├── AnnotatedGenericWithSerializerTypeDeserializer.java │ │ ├── AnnotatedGenericWithSerializerTypeSerializer.java │ │ ├── AnnotatedWithSerializerType.java │ │ ├── AnnotatedWithSerializerTypeDeserializer.java │ │ ├── AnnotatedWithSerializerTypeSerializer.java │ │ ├── AnnotatedWithSerializerTypeSerializerOverride.java │ │ ├── Author.java │ │ ├── Box.java │ │ ├── BoxWithAnnotations.java │ │ ├── Containee.java │ │ ├── ContaineeDeserializer.java │ │ ├── ContaineeSerializer.java │ │ ├── Container.java │ │ ├── Crate.java │ │ ├── CrateDeserializer.java │ │ ├── CrateDeserializerWithConversion.java │ │ ├── CrateInner.java │ │ ├── CrateJsonObjectDeserializer.java │ │ ├── CrateSerializer.java │ │ ├── CrateSerializerWithConversion.java │ │ ├── ExplicitJsonbSerializer.java │ │ ├── GenericPropertyPojo.java │ │ ├── GenericPropertyPojoSerializer.java │ │ ├── ImplicitJsonbSerializer.java │ │ ├── NumberDeserializer.java │ │ ├── NumberSerializer.java │ │ ├── Pokemon.java │ │ ├── RecursiveDeserializer.java │ │ ├── RecursiveSerializer.java │ │ ├── SimpleAnnotatedSerializedArrayContainer.java │ │ ├── SimpleContainer.java │ │ ├── SimpleContainerArrayDeserializer.java │ │ ├── SimpleContainerArraySerializer.java │ │ ├── StringPaddingSerializer.java │ │ ├── StringWrapper.java │ │ ├── SupertypeSerializerPojo.java │ │ └── Trainer.java │ └── resources/ │ ├── META-INF/ │ │ └── beans.xml │ ├── jndi.properties │ ├── logging.properties │ ├── test.policy │ └── yasson-messages_cs.properties ├── yasson-jmh/ │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── eclipse/ │ └── yasson/ │ └── jmh/ │ ├── CollectionsTest.java │ ├── ScalarDataTest.java │ ├── TenPropertySerializationTest.java │ └── model/ │ ├── CollectionsData.java │ ├── ScalarData.java │ └── TenPropertyData.java └── yasson-tck/ ├── .gitignore └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. If there is an Exception, please include the full stack trace. **To Reproduce** Steps to reproduce the bug **Expected behavior** A clear and concise description of what you expected to happen. **System information:** - OS: [e.g. Linux, Windows, Mac] - Java Version: [e.g. 8, 11] - Yasson Version: [e.g. 1.0.5] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/other-issue.md ================================================ --- name: Other issue about: Not a bug or a feature request title: '' labels: '' assignees: '' --- ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: daily # TODO - add maven dependabot if community agrees it's useful ================================================ FILE: .github/workflows/maven.yml ================================================ # # Copyright (c) 2021, 2026 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # name: Yasson on: push: branches: - 'main' - '*-RELEASE' pull_request: branches: - 'main' - '*-RELEASE' jobs: build: name: Test on JDK ${{ matrix.java_version }} runs-on: ubuntu-latest strategy: matrix: java_version: [ 11, 17, 21 ] steps: - name: Checkout for build uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up compile JDK uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: #Compile java needs to be the highest to ensure proper compilation of the multi-release jar distribution: 'temurin' java-version: 17 cache: 'maven' - name: Copyright run: bash etc/copyright.sh - name: Checkstyle run: mvn -B checkstyle:checkstyle - name: Yasson install run: mvn -U -C clean install -DskipTests - name: Yasson tests run: mvn -U -B -C -Dmaven.javadoc.skip=true verify - name: JSONB-API TCK run: cd yasson-tck && mvn -U -B test -DargLine="-Djava.locale.providers=COMPAT" ================================================ FILE: .gitignore ================================================ /target/ /target-tck/ .classpath .project .idea/ .settings/ /.DS_Store bin/ .envrc .vscode/ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Eclipse Yasson Thanks for your interest in this project. ## Project description Eclipse Yasson is a Java framework which provides a standard binding layer between Java classes and JSON documents. This is similar to what JAXB is doing in the XML world. Yasson is an official reference implementation of JSON Binding (JSR-367). * https://projects.eclipse.org/projects/ee4j.yasson ## Developer resources Information regarding source code management, builds, coding standards, and more. * https://projects.eclipse.org/projects/ee4j.yasson/developer The project maintains the following source code repositories * https://github.com/eclipse/yasson * https://github.com/eclipse-ee4j/yasson ## Eclipse Contributor Agreement Before your contribution can be accepted by the project team contributors must electronically sign the Eclipse Contributor Agreement (ECA). * http://www.eclipse.org/legal/ECA.php Commits that are provided by non-committers must have a Signed-off-by field in the footer indicating that the author is aware of the terms by which the contribution has been provided to the project. The non-committer must additionally have an Eclipse Foundation account and must have a signed Eclipse Contributor Agreement (ECA) on file. For more information, please see the Eclipse Committer Handbook: https://www.eclipse.org/projects/handbook/#resources-commit ## Contact Contact the project developers via the project's "dev" list. * https://dev.eclipse.org/mailman/listinfo/yasson-dev ================================================ FILE: LICENSE.md ================================================ # Eclipse Public License - v 2.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS “Contribution” means: a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. “Contributor” means any person or entity that Distributes the Program. “Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. “Program” means the Contributions Distributed in accordance with this Agreement. “Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. “Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. “Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. “Distribute” means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. “Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. “Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 3. REQUIREMENTS 3.1 If a Contributor Distributes the Program in any form, then: a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 3.2 When the Program is Distributed as Source Code: a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and b) a copy of this Agreement must be included with each copy of the Program. 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (‘notices’) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED 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. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. Exhibit A – Form of Secondary Licenses Notice “This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.” Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. # Eclipse Distribution License - v 1.0 Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: NOTICE.md ================================================ # Notices for Eclipse Yasson This content is produced and maintained by the Eclipse Yasson project. * Project home: https://projects.eclipse.org/projects/ee4j.yasson ## Trademarks Eclipse Yasson is a trademark of the Eclipse Foundation. ## Copyright All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. ## Declared Project Licenses This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0 which is available at http://www.eclipse.org/legal/epl-v20.html, or the Eclipse Distribution License v. 1.0 which is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause ## Source Code The project maintains the following source code repositories: * https://github.com/eclipse/yasson * https://github.com/eclipse-ee4j/yasson ## Third-party Content cdi-api 2.0 (JSR 365: Contexts and Dependency Injection for Java (2.0) ## Cryptography Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. ================================================ FILE: README.md ================================================ # Eclipse Yasson [![Maven Central](https://img.shields.io/maven-central/v/org.eclipse/yasson.svg?label=Maven%20Central)](https://mvnrepository.com/artifact/org.eclipse/yasson) [![Gitter](https://badges.gitter.im/eclipse/yasson.svg)](https://gitter.im/eclipse/yasson) [![Javadocs](https://www.javadoc.io/badge/org.eclipse/yasson.svg)](https://www.javadoc.io/doc/org.eclipse/yasson) [![Build Status](https://github.com/eclipse-ee4j/yasson/actions/workflows/maven.yml/badge.svg?branch=main)](https://github.com/eclipse-ee4j/yasson/actions/workflows/maven.yml?branch=main) [![License](https://img.shields.io/badge/License-EPL%202.0-green.svg)](https://opensource.org/licenses/EPL-2.0) Yasson is a Java framework which provides a standard binding layer between Java classes and JSON documents. This is similar to what JAXB is doing in the XML world. Yasson is an official reference implementation of JSON Binding ([JSR-367](https://jcp.org/en/jsr/detail?id=367)). It defines a **default mapping** algorithm for converting existing Java classes to JSON suitable for the most cases: ```java Jsonb jsonb = JsonbBuilder.create(); String result = jsonb.toJson(someObject); ``` For whom it's not enough it provides rich customization abilities through a set of **annotations** and rich **programmatic API**: ```java // Create custom configuration JsonbConfig config = new JsonbConfig() .withNullValues(true) .withFormatting(true); // Create Jsonb with custom configuration Jsonb jsonb = JsonbBuilder.create(config); // Use it! String result = jsonb.toJson(someObject); ``` ## Questions? Something not working right? Have an idea for an enhancement? Get in touch with the Yasson community in the following ways: - [Gitter](https://gitter.im/eclipse/yasson): a free instant-messaging platform (similar to Slack) that anyone can join. - [Stackoverflow](https://stackoverflow.com/questions/tagged/yasson): As a question tagged `[jsonb-api]` and `[yasson]` - [Github Issues](https://github.com/eclipse-ee4j/yasson/issues/new): Open issues for enhancement ideas or bug reports ## Licenses - [Eclipse Distribution License 1.0 (BSD)](https://projects.eclipse.org/content/eclipse-distribution-license-1.0-bsd) - [Eclipse Public License 2.0](https://projects.eclipse.org/content/eclipse-public-license-2.0) ## Links - Yasson home page: https://projects.eclipse.org/projects/ee4j.yasson - JSON-B official web site: https://jakartaee.github.io/jsonb-api/ - JSON-B API & spec project: https://github.com/jakartaee/jsonb-api - JSR-367 page on JCP site: https://jcp.org/en/jsr/detail?id=367 ================================================ FILE: etc/checkstyle-suppressions.xml ================================================ ================================================ FILE: etc/checkstyle.xml ================================================ ================================================ FILE: etc/copyright-exclude.txt ================================================ .iml .apt .args .bundle .class .ddl .exe .gif .gitignore .ico .jar .jks .jpg .json .mm .ods .png .svg .war .zip .dat .md .p12 .txt .mf .pem .p8 .pkcs8.pem .p12 .bin .vm .policy ================================================ FILE: etc/copyright.sh ================================================ #!/bin/bash -x # # Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # die(){ echo "${1}" ; exit 1 ;} readonly RESULT_FILE="target/copyright-check.txt" mkdir target mvn -q validate -Pcopyright \ > ${RESULT_FILE} || (cat ${RESULT_FILE}; die "Error running the Maven command") grep -i "copyright" ${RESULT_FILE} \ && die "COPYRIGHT ERROR" || echo "COPYRIGHT OK" ================================================ FILE: etc/copyright.txt ================================================ /* * Copyright (c) YYYY Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ ================================================ FILE: etc/delivery-checks.sh ================================================ #!/bin/bash # # Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # mvn checkstyle:checkstyle && \ etc/copyright.sh && \ echo "Completed normally" ================================================ FILE: pom.xml ================================================ 4.0.0 org.eclipse.ee4j project 2.0.2 org.eclipse yasson 3.0.5-SNAPSHOT jar Yasson Eclipse Yasson. Reference implementation of JSR-367 (JSON-B). https://projects.eclipse.org/projects/ee4j.yasson UTF-8 11 ${maven.compiler.release} yasson-maven2-staging 2.2 3.0.0 6.0.0 4.1.0 2.2.0 3.0.1 2.1.3 1.1.7 5.10.2 6.0.0.Beta1 3.6.0 3.2.0 4.8.5.0 2.4 5.1.9 3.3.1 3.13.0 3.4.1 3.4.1 3.6.3 3.2.5 jakarta.enterprise jakarta.enterprise.cdi-api ${jakarta.enterprise.cdi-api.version} jakarta.el jakarta.el-api ${jakarta.el-api.version} jakarta.interceptor jakarta.interceptor-api ${jakarta.interceptor-api.version} jakarta.annotation jakarta.annotation-api ${jakarta.annotation-api.version} jakarta.json.bind jakarta.json.bind-api ${jakarta.json.bind.version} jakarta.json jakarta.json-api ${jakarta.json.version} org.eclipse.parsson parsson ${jakarta.parson.version} jakarta.enterprise jakarta.enterprise.cdi-api true provided org.jboss.weld.se weld-se-core ${weld-se-core.version} test org.junit.jupiter junit-jupiter-api ${junit-jupiter.version} test org.junit.jupiter junit-jupiter-engine ${junit-jupiter.version} test org.hamcrest hamcrest ${hamcrest.version} test Oracle Corporation http://www.oracle.com/ github https://github.com/eclipse-ee4j/yasson/issues Yasson mailing list yasson-dev@eclipse.org https://dev.eclipse.org/mailman/listinfo/yasson-dev https://dev.eclipse.org/mailman/listinfo/yasson-dev https://dev.eclipse.org/mhonarc/lists/yasson-dev/ Eclipse Public License v. 2.0 http://www.eclipse.org/legal/epl-v20.html repo Standard Eclipse License Eclipse Distribution License v. 1.0 http://www.eclipse.org/org/documents/edl-v10.php repo Standard Eclipse Distribution License scm:git:ssh://git@github.com/eclipse-ee4j/yasson.git scm:git:ssh://git@github.com/eclipse-ee4j/yasson.git https://github.com/eclipse-ee4j/yasson.git HEAD dmitry.kornilov@oracle.com maiden168 Dmitry Kornilov Oracle JSON Binding 1.0 Spec Lead CET roman.grigoriadi@oracle.com roman.grigoriadi Roman Grigoriadi Oracle JSON Binding 1.0 Developer CET david.k.kral@oracle.com david.kral David Kral Oracle JSON Binding 1.0 Developer CET andy.guibert@gmail.com aguibert Andy Guibert IBM JSON Binding 1.0 Developer CST checkstyle org.apache.maven.plugins maven-checkstyle-plugin run-checkstyle check validate spotbugs com.github.spotbugs spotbugs-maven-plugin ${spotbugs-maven-plugin.version} Low analyze-compile compile check copyright org.glassfish.copyright glassfish-copyright-maven-plugin true print-copyright copyright validate check-copyright check validate jdk16 [16,) org.apache.maven.plugins maven-compiler-plugin default-testCompile 16 16 ${project.basedir}/src/test/java ${project.basedir}/src/test/java16 org.apache.maven.plugins maven-failsafe-plugin integration-test verify **/RecordTest.java ${project.artifactId} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} default-compile compile 11 11 11 default-testCompile 11 multi-release-compile-16 compile 16 ${project.basedir}/src/main/java16 true -Xlint:all org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} ${project.build.outputDirectory}/META-INF/MANIFEST.MF true org.codehaus.mojo buildnumber-maven-plugin ${buildnumber-maven-plugin.version} {0,date,MM/dd/yyyy hh:mm aa} timestamp validate create org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} ]]> org.apache.felix maven-bundle-plugin ${maven-bundle-plugin.version} osgi-bundle prepare-package manifest true ${project.name} ${project.artifactId} ${project.version} org.eclipse.yasson;version=${project.version} org.eclipse.yasson.*;version=${project.version} true jakarta.enterprise.context.spi;version=!;resolution:="optional", jakarta.enterprise.inject.spi;version=!;resolution:="optional", javax.naming;resolution:="optional", java.beans;resolution:="optional", * osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))" org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} default-test test test false **/JavaxNamingExcludedTest.java **/AnnotationIntrospectorWithoutOptionalModulesTest.java **/*Record* --add-reads org.eclipse.yasson=ALL-UNNAMED --add-opens org.eclipse.yasson/org.eclipse.yasson.internal.cdi=ALL-UNNAMED --add-exports org.eclipse.yasson/org.eclipse.yasson.internal.cdi=java.naming optional-modules-excluded test test --limit-modules java.base,java.logging,java.sql,jakarta.json.bind,jakarta.json,java.management,jdk.localedata,org.eclipse.parsson,org.eclipse.yasson **/JavaxNamingExcludedTest.class **/AnnotationIntrospectorWithoutOptionalModulesTest.class org.apache.maven.plugins maven-enforcer-plugin ${maven-enforcer-plugin.version} enforce-versions enforce [11,) [3.6.0,) org.codehaus.mojo build-helper-maven-plugin ${build-helper-maven-plugin.version} add-resource generate-resources add-resource ${basedir} META-INF LICENSE.md NOTICE.md org.apache.maven.plugins maven-checkstyle-plugin ${maven-checkstyle-plugin.version} etc/checkstyle.xml etc/checkstyle-suppressions.xml true true false org.glassfish.copyright glassfish-copyright-maven-plugin ${glassfish-copyright-maven-plugin.version} etc/copyright.txt etc/copyright-exclude.txt git false true true false true org.apache.maven.plugins maven-compiler-plugin org.apache.maven.plugins maven-jar-plugin org.codehaus.mojo buildnumber-maven-plugin org.apache.maven.plugins maven-dependency-plugin org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-enforcer-plugin org.codehaus.mojo build-helper-maven-plugin org.apache.felix maven-bundle-plugin ================================================ FILE: src/main/java/module-info.java ================================================ /* * Copyright (c) 2017, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /** * Yasson, the implementation of the Jakarta JSON Binding. */ module org.eclipse.yasson { requires jakarta.json; requires jakarta.json.bind; requires java.logging; requires static java.xml; requires static java.naming; requires static java.sql; requires static java.desktop; requires static jakarta.cdi; exports org.eclipse.yasson; exports org.eclipse.yasson.spi; provides jakarta.json.bind.spi.JsonbProvider with org.eclipse.yasson.JsonBindingProvider; uses org.eclipse.yasson.spi.JsonbComponentInstanceCreator; } ================================================ FILE: src/main/java/org/eclipse/yasson/FieldAccessStrategy.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import java.lang.reflect.Field; import java.lang.reflect.Method; import jakarta.json.bind.config.PropertyVisibilityStrategy; /** *

Strategy that can be used to force always using fields instead of getters setters for getting / setting value.

*

Suggested approach is to use default visibility strategy, which will use public getters / setters, or field * if it is public.

* *

Please consider, that forcing accessing fields will in most cases (when field is not public) * result in calling {@link Field#setAccessible(boolean)} to break into clients code. * This may cause problems if client code is loaded as JPMS (Java Platform Module System) module, as OSGi module or * when SecurityManager is turned on.

*/ public class FieldAccessStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return true; } @Override public boolean isVisible(Method method) { return false; } } ================================================ FILE: src/main/java/org/eclipse/yasson/ImplementationClass.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marks an interface with implementation class, which should be used for deserialiation. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface ImplementationClass { /** * Class, which will be used as implementation for annotated member. * * @return Serializaer to use. */ Class value(); } ================================================ FILE: src/main/java/org/eclipse/yasson/JsonBindingProvider.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.spi.JsonbProvider; import org.eclipse.yasson.internal.JsonBindingBuilder; /** * JsonbProvider implementation. */ public class JsonBindingProvider extends JsonbProvider { @Override public JsonbBuilder create() { return new JsonBindingBuilder(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/YassonConfig.java ================================================ /* * Copyright (c) 2019, 2022 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import java.util.Map; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.serializer.JsonbSerializer; /** * Custom properties for configuring Yasson outside of the specification {@link jakarta.json.bind.JsonbConfig} scope. */ public class YassonConfig extends JsonbConfig { /** * @see #withFailOnUnknownProperties(boolean) */ public static final String FAIL_ON_UNKNOWN_PROPERTIES = "jsonb.fail-on-unknown-properties"; /** * @see #withUserTypeMapping(java.util.Map) */ public static final String USER_TYPE_MAPPING = "jsonb.user-type-mapping"; /** * @see #withZeroTimeParseDefaulting(boolean) */ public static final String ZERO_TIME_PARSE_DEFAULTING = "jsonb.zero-time-defaulting"; /** * @see #withNullRootSerializer(jakarta.json.bind.serializer.JsonbSerializer) */ public static final String NULL_ROOT_SERIALIZER = "yasson.null-root-serializer"; /** * @see #withEagerParsing(Class...) */ public static final String EAGER_PARSE_CLASSES = "yasson.eager-parse-classes"; /** * @see #withForceMapArraySerializerForNullKeys(boolean) */ public static final String FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS = "yasson.force-map-array-serializer-for-null-keys"; /** * @see #withTimeInMillisAsAString(boolean) */ public static final String DATE_TIME_IN_MILLIS_AS_A_STRING = "yasson.time-in-millis-as-a-string"; /** * Property used to specify behaviour on deserialization when JSON document contains properties * which doesn't exist in the target class. Default value is 'false'. * @param failOnUnknownProperties Whether or not to fail if unknown properties are encountered * @return This YassonConfig instance */ public YassonConfig withFailOnUnknownProperties(boolean failOnUnknownProperties) { setProperty(FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties); return this; } /** * User type mapping for map interface to implementation classes. * @param mapping A map of interface to implementation class mappings * @return This YassonConfig instance */ public YassonConfig withUserTypeMapping(Map, Class> mapping) { setProperty(USER_TYPE_MAPPING, mapping); return this; } /** *

Makes parsing dates defaulting to zero hour, minute and second. * This will made available to parse patterns like yyyy.MM.dd to * {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate} * or even {@link java.time.ZonedDateTime}. *

If time zone is not set in the pattern then UTC time zone is used. * So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed * to instant {@link java.time.Instant} or {@link java.time.ZonedDateTime}. * @param defaultZeroHour Whether or not to default parsing dates to the zero hour * @return This YassonConfig instance */ public YassonConfig withZeroTimeParseDefaulting(boolean defaultZeroHour) { setProperty(ZERO_TIME_PARSE_DEFAULTING, defaultZeroHour); return this; } /** * Serializer to use when object provided to {@link jakarta.json.bind.Jsonb#toJson(Object)} is {@code null} or an empty * Optional. Must be instance of {@link jakarta.json.bind.serializer.JsonbSerializer}{@code }. Its obj value * will be respective parameter. * @param nullSerializer JsonbSerializer instance to use for serializing null root values * @return This YassonConfig instance */ public YassonConfig withNullRootSerializer(JsonbSerializer nullSerializer) { setProperty(NULL_ROOT_SERIALIZER, nullSerializer); return this; } /** * @param classes A list of classes to eagerly parse upon creation of the Jsonb instance used with this configuration. * @return This YassonConfig instance */ public YassonConfig withEagerParsing(Class... classes) { setProperty(EAGER_PARSE_CLASSES, classes); return this; } /** * Property needed to make MapToEntriesArraySerializer the serializer used * when a null key is found in the map. Default value is false. * @param value true to force the use of the MapToEntriesArraySerializer if * a null key is detected in the map, false to work as before * @return This YassonConfig instance */ public YassonConfig withForceMapArraySerializerForNullKeys(boolean value) { setProperty(FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS, value); return this; } /** * It is required to handle time millisecond format as a number. See * {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS}. It is possible to override this and force * Yasson to handle it as a String, by using this method. * * @param value whether to treat dates formatted by {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS} * as a String. Default value is {@code false}. * @return This YassonConfig instance * @since 3.0.0 */ public YassonConfig withTimeInMillisAsAString(boolean value) { setProperty(DATE_TIME_IN_MILLIS_AS_A_STRING, value); return this; } } ================================================ FILE: src/main/java/org/eclipse/yasson/YassonJsonb.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import java.lang.reflect.Type; import jakarta.json.JsonStructure; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; /** * Adds methods to Jsonb that are operating directly with {@link JsonGenerator} or {@link JsonParser} types. *

* {@link jakarta.json.spi.JsonProvider} operates on top of the * {@link java.io.InputStream} / {@link java.io.OutputStream} or {@link java.io.Reader} / {@link java.io.Writer} * and creates generator / parser instances during runtime. *

*

* This interface accepts instantiated generators and parsers with different input / output sources. *

*/ public interface YassonJsonb extends jakarta.json.bind.Jsonb { /** * Reads in a JSON data with a specified {@link JsonParser} and return the * resulting content tree. Provided json parser must be fully initialized, * no further configurations will be applied. * * @param jsonParser The json parser instance to be used to read JSON data. * @param type Type of the content tree's root object. * @param Type of the content tree's root object. * @return the newly created root object of the java content tree * @throws JsonbException If any unexpected error(s) occur(s) during deserialization. */ T fromJson(JsonParser jsonParser, Class type) throws JsonbException; /** * Reads in a JSON data with a specified {@link JsonParser} and return the * resulting content tree. Provided json parser must be fully initialized, * no further configurations will be applied. * * @param jsonParser The json parser instance to be used to read JSON data. * @param runtimeType Runtime type of the content tree's root object. * @param Type of the content tree's root object. * @return the newly created root object of the java content tree * @throws JsonbException If any unexpected error(s) occur(s) during deserialization. */ T fromJson(JsonParser jsonParser, Type runtimeType) throws JsonbException; /** * Reads a {@link JsonStructure} and and converts it into * resulting java content tree. * * @param jsonStructure {@link JsonStructure} to be used as a source for conversion. * @param type Type of the content tree's root object. * @param Type of the content tree's root object. * @return the newly created root object of the java content tree * @throws JsonbException If any unexpected error(s) occur(s) during conversion. */ T fromJsonStructure(JsonStructure jsonStructure, Class type) throws JsonbException; /** * Reads a {@link JsonStructure} and and converts it into * resulting java content tree. * * @param jsonStructure {@link JsonStructure} to be used as a source for conversion. * @param runtimeType Runtime type of the content tree's root object. * @param Type of the content tree's root object. * @return the newly created root object of the java content tree * @throws JsonbException If any unexpected error(s) occur(s) during deserialization. */ T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException; /** * Writes the object content tree with a specified {@link JsonGenerator}. * Provided json generator must be fully initialized, no further configurations are applied. * * @param object The object content tree to be serialized. * @param jsonGenerator The json generator to write JSON data. The generator is not closed * on a completion for further interaction. * @throws JsonbException If any unexpected problem occurs during the * serialization. * @since JSON Binding 1.0 */ void toJson(Object object, JsonGenerator jsonGenerator) throws JsonbException; /** * Writes the object content tree with a specified {@link JsonGenerator}. * Provided json generator must be fully initialized, no further configurations are applied. * * @param object The object content tree to be serialized. * @param runtimeType Runtime type of the content tree's root object. * @param jsonGenerator The json generator to write JSON data. The generator is not closed * on a completion for further interaction. * @throws JsonbException If any unexpected problem occurs during the * serialization. * @since JSON Binding 1.0 */ void toJson(Object object, Type runtimeType, JsonGenerator jsonGenerator) throws JsonbException; /** * Serializes the object content tree to a {@link jakarta.json.JsonStructure}. * * @param object The object content tree to be serialized. * @return The {@link JsonStructure} serialized from java content tree. * @throws JsonbException If any unexpected problem occurs during the * serialization. * @since JSON Binding 1.0 */ JsonStructure toJsonStructure(Object object) throws JsonbException; /** * Serializes the object content tree to a {@link jakarta.json.JsonStructure}. * * @param object The object content tree to be serialized. * @param runtimeType Runtime type of the content tree's root object. * @return The {@link JsonStructure} serialized from java content tree. * @throws JsonbException If any unexpected problem occurs during the * serialization. * @since JSON Binding 1.0 */ JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException; } ================================================ FILE: src/main/java/org/eclipse/yasson/YassonProperties.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; /** * @deprecated Use {@link YassonConfig} instead */ @Deprecated public class YassonProperties { private YassonProperties() { throw new IllegalStateException("Util classes cannot be instantiated."); } /** * @deprecated * @see YassonConfig#withFailOnUnknownProperties(boolean) */ public static final String FAIL_ON_UNKNOWN_PROPERTIES = YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES; /** * @deprecated * @see YassonConfig#withUserTypeMapping(java.util.Map) */ public static final String USER_TYPE_MAPPING = YassonConfig.USER_TYPE_MAPPING; /** * @deprecated * @see YassonConfig#withZeroTimeParseDefaulting(boolean) */ public static final String ZERO_TIME_PARSE_DEFAULTING = YassonConfig.ZERO_TIME_PARSE_DEFAULTING; /** * @deprecated * @see YassonConfig#withNullRootSerializer(jakarta.json.bind.serializer.JsonbSerializer) */ public static final String NULL_ROOT_SERIALIZER = YassonConfig.NULL_ROOT_SERIALIZER; } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/AnnotationFinder.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Logger; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Finds an annotation including inherited annotations (e.g. meta-annotations). */ class AnnotationFinder { private static final String CONSTRUCTOR_PROPERTIES_ANNOTATION = "java.beans.ConstructorProperties"; private static final Logger LOGGER = Logger.getLogger(AnnotationFinder.class.getName()); private final String annotationClassName; private final Class annotationClass; // may be null /** * Gets the {@link AnnotationFinder} for the given Annotation-Type. * * @param annotation {@link Class}, that is a sub-type of {@link Annotation} * @return {@link AnnotationFinder} */ public static AnnotationFinder findAnnotation(Class annotation) { return findAnnotationByName(annotation.getName()); } /** * Gets the {@link AnnotationFinder} for the given Annotation-Type Name. * * @param annotationClassName {@link String}, that is a sub-type of {@link Annotation} * @return {@link AnnotationFinder} */ public static AnnotationFinder findAnnotationByName(String annotationClassName) { return new AnnotationFinder(annotationClassName, getOptionalAnnotationClass(annotationClassName)); } /** * Gets the {@link AnnotationFinder} for @ConstructorProperties-Annotation. * * @return {@link AnnotationFinder} */ public static AnnotationFinder findConstructorProperties() { return findAnnotationByName(CONSTRUCTOR_PROPERTIES_ANNOTATION); } private AnnotationFinder(String annotationClassName, Class annotationClass) { this.annotationClassName = annotationClassName; this.annotationClass = annotationClass; } @SuppressWarnings("unchecked") public T in(Annotation[] annotations) { if (annotationClass == null) { return null; } return (T) findAnnotation(annotations, annotationClass, new HashSet<>()); } /** * Looks for the annotation {@link #in(Annotation[])}
* and executes the "value" Method of it dynamically. * * @param annotations - Array of {@link Annotation}n. * @return {@link Object} */ public Object valueIn(Annotation[] annotations) { return invocateValueMethod(in(annotations)); } private Object invocateValueMethod(Annotation annotation) { if (annotation == null) { return null; } try { return annotation.annotationType().getMethod("value").invoke(annotation); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { String message = Messages .getMessage(MessageKeys.MISSING_VALUE_PROPERTY_IN_ANNOTATION, annotation.annotationType().getName()); LOGGER.finest(message); return null; } } @SuppressWarnings("unchecked") private static Class getOptionalAnnotationClass(String classname) { try { return (Class) Class.forName(classname); } catch (ClassNotFoundException e) { String message = Messages.getMessage(MessageKeys.ANNOTATION_NOT_AVAILABLE, classname); LOGGER.finest(message); return null; } } /** * Searches for annotation, collects processed, to avoid StackOverflow. */ // "static" to use it in a hybrid procedural and object oriented manner. @SuppressWarnings("unchecked") public static T findAnnotation(Annotation[] declaredAnnotations, Class annotationClass, Set processed) { for (Annotation candidate : declaredAnnotations) { final Class annType = candidate.annotationType(); if (annType.equals(annotationClass)) { return (T) candidate; } processed.add(candidate); final List inheritedAnnotations = new ArrayList<>(Arrays.asList(annType.getDeclaredAnnotations())); inheritedAnnotations.removeAll(processed); if (inheritedAnnotations.size() > 0) { final T inherited = findAnnotation(inheritedAnnotations.toArray(new Annotation[inheritedAnnotations.size()]), annotationClass, processed); if (inherited != null) { return inherited; } } } return null; } @Override public String toString() { return "AnnotationFinder [annotationClassName=" + annotationClassName + ", annotationClass=" + annotationClass + "]"; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java ================================================ /* * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.security.AccessController; import java.security.PrivilegedAction; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Queue; import java.util.Set; import jakarta.json.bind.JsonbException; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbNillable; import jakarta.json.bind.annotation.JsonbNumberFormat; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbPropertyOrder; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTransient; import jakarta.json.bind.annotation.JsonbTypeAdapter; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeInfo; import jakarta.json.bind.annotation.JsonbTypeSerializer; import jakarta.json.bind.annotation.JsonbVisibility; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.bind.config.PropertyVisibilityStrategy; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import org.eclipse.yasson.ImplementationClass; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.components.SerializerBinding; import org.eclipse.yasson.internal.model.AnnotationTarget; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbAnnotatedElement; import org.eclipse.yasson.internal.model.JsonbAnnotatedElement.AnnotationWrapper; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.model.Property; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Introspects configuration on classes and their properties by reading annotations. */ public class AnnotationIntrospector { // private static final Set> OPTIONALS = Set.of(Optional.class, // OptionalInt.class, // OptionalLong.class, // OptionalDouble.class); private final JsonbContext jsonbContext; private final ConstructorPropertiesAnnotationIntrospector constructorPropertiesIntrospector; private static final Set> REPEATABLE = Set.of(JsonbTypeInfo.class); /** * Annotations to report exception when used in combination with {@link JsonbTransient}. */ private static final List> TRANSIENT_INCOMPATIBLE = Arrays.asList(JsonbDateFormat.class, JsonbNumberFormat.class, JsonbProperty.class, JsonbTypeAdapter.class, JsonbTypeSerializer.class, JsonbTypeDeserializer.class); /** * Creates annotation introspecting component passing {@link JsonbContext} inside. * * @param jsonbContext mandatory */ public AnnotationIntrospector(JsonbContext jsonbContext) { Objects.requireNonNull(jsonbContext); this.jsonbContext = jsonbContext; this.constructorPropertiesIntrospector = ConstructorPropertiesAnnotationIntrospector.forContext(jsonbContext); } /** * Gets a name of property for JSON marshalling. * Can be different writeName for same property. * * @param property property representation - field, getter, setter (not null) * @return read name */ public String getJsonbPropertyJsonWriteName(Property property) { Objects.requireNonNull(property); return getJsonbPropertyCustomizedName(property, property.getGetterElement()); } /** * Gets a name of property for JSON unmarshalling. * Can be different from writeName for same property. * * @param property property representation - field, getter, setter (not null) * @return write name */ public String getJsonbPropertyJsonReadName(Property property) { Objects.requireNonNull(property); return getJsonbPropertyCustomizedName(property, property.getSetterElement()); } private String getJsonbPropertyCustomizedName(Property property, JsonbAnnotatedElement methodElement) { JsonbProperty methodAnnotation = getMethodAnnotation(JsonbProperty.class, methodElement); if (methodAnnotation != null && !methodAnnotation.value().isEmpty()) { return methodAnnotation.value(); } //in case of property name getter/setter override field value JsonbProperty fieldAnnotation = getFieldAnnotation(JsonbProperty.class, property.getFieldElement()); if (fieldAnnotation != null && !fieldAnnotation.value().isEmpty()) { return fieldAnnotation.value(); } return null; } /** * Searches for JsonbCreator annotation on constructors and static methods. * * @param clazz class to search * @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation, * if set and no {@code JsonbProperty} annotations are present. * @return JsonbCreator metadata object */ public JsonbCreator getCreator(Class clazz, PropertyNamingStrategy propertyNamingStrategy) { JsonbCreator jsonbCreator = null; Constructor[] declaredConstructors = AccessController.doPrivileged((PrivilegedAction[]>) clazz::getDeclaredConstructors); for (Constructor constructor : declaredConstructors) { final jakarta.json.bind.annotation.JsonbCreator annot = findAnnotation(constructor.getDeclaredAnnotations(), jakarta.json.bind.annotation.JsonbCreator.class); if (annot != null) { jsonbCreator = createJsonbCreator(constructor, jsonbCreator, clazz, propertyNamingStrategy); } } Method[] declaredMethods = AccessController.doPrivileged((PrivilegedAction) clazz::getDeclaredMethods); for (Method method : declaredMethods) { final jakarta.json.bind.annotation.JsonbCreator annot = findAnnotation(method.getDeclaredAnnotations(), jakarta.json.bind.annotation.JsonbCreator.class); if (annot != null && Modifier.isStatic(method.getModifiers())) { if (!clazz.equals(method.getReturnType())) { throw new JsonbException(Messages.getMessage(MessageKeys.INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE, method, clazz)); } jsonbCreator = createJsonbCreator(method, jsonbCreator, clazz, propertyNamingStrategy); } } if (jsonbCreator == null) { jsonbCreator = ClassMultiReleaseExtension.findCreator(clazz, declaredConstructors, this, propertyNamingStrategy); if (jsonbCreator == null) { jsonbCreator = constructorPropertiesIntrospector.getCreator(declaredConstructors); } } return jsonbCreator; } JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class clazz, PropertyNamingStrategy propertyNamingStrategy) { if (existing != null) { throw new JsonbException(Messages.getMessage(MessageKeys.MULTIPLE_JSONB_CREATORS, clazz)); } final Parameter[] parameters = executable.getParameters(); CreatorModel[] creatorModels = new CreatorModel[parameters.length]; for (int i = 0; i < parameters.length; i++) { final Parameter parameter = parameters[i]; final JsonbProperty jsonbPropertyAnnotation = parameter.getAnnotation(JsonbProperty.class); if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isEmpty()) { creatorModels[i] = new CreatorModel(jsonbPropertyAnnotation.value(), parameter, executable, jsonbContext); } else { final String translatedParameterName = propertyNamingStrategy.translateName(parameter.getName()); creatorModels[i] = new CreatorModel(translatedParameterName, parameter, executable, jsonbContext); } } return new JsonbCreator(executable, creatorModels); } /** * Checks for {@link JsonbAdapter} on a property. * * @param property property not null * @return components info */ public AdapterBinding getAdapterBinding(Property property) { Objects.requireNonNull(property); JsonbTypeAdapter adapterAnnotation = getAnnotationFromProperty(JsonbTypeAdapter.class, property) .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeAdapter.class)); if (adapterAnnotation == null) { return null; } return getAdapterBindingFromAnnotation(adapterAnnotation, ReflectionUtils.getOptionalRawType(property.getPropertyType())); } /** * Checks for {@link JsonbAdapter} on a type. * * @param clsElement type not null * @return components info */ public AdapterBinding getAdapterBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); JsonbTypeAdapter adapterAnnotation = clsElement.getElement().getAnnotation(JsonbTypeAdapter.class); if (adapterAnnotation == null) { return null; } return getAdapterBindingFromAnnotation(adapterAnnotation, Optional.ofNullable(clsElement.getElement())); } private AdapterBinding getAdapterBindingFromAnnotation(JsonbTypeAdapter adapterAnnotation, Optional> expectedClass) { final Class adapterClass = adapterAnnotation.value(); final AdapterBinding adapterBinding = jsonbContext.getComponentMatcher().introspectAdapterBinding(adapterClass, null); if (expectedClass.isPresent() && !( ReflectionUtils.getRawType(adapterBinding.getBindingType()).isAssignableFrom(expectedClass.get()))) { throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_INCOMPATIBLE, adapterBinding.getBindingType(), expectedClass.get())); } return adapterBinding; } /** * Checks for {@link JsonbDeserializer} on a property. * * @param property property not null * @return components info */ public DeserializerBinding getDeserializerBinding(Property property) { Objects.requireNonNull(property); JsonbTypeDeserializer deserializerAnnotation = getAnnotationFromProperty(JsonbTypeDeserializer.class, property) .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeDeserializer.class)); if (deserializerAnnotation == null) { return null; } final Class deserializerClass = deserializerAnnotation.value(); return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); } /** * Checks for {@link JsonbDeserializer} on a {@link Parameter}. * * @param parameter parameter not null * @return components info */ public DeserializerBinding getDeserializerBinding(Parameter parameter) { Objects.requireNonNull(parameter); JsonbTypeDeserializer deserializerAnnotation = Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeDeserializer.class)) .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeDeserializer.class)); if (deserializerAnnotation == null) { return null; } final Class deserializerClass = deserializerAnnotation.value(); return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); } /** * Checks for {@link JsonbAdapter} on a {@link Parameter}. * * @param parameter parameter not null * @return components info */ public AdapterBinding getAdapterBinding(Parameter parameter) { Objects.requireNonNull(parameter); JsonbTypeAdapter adapter = Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeAdapter.class)) .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeAdapter.class)); if (adapter == null) { return null; } return getAdapterBindingFromAnnotation(adapter, ReflectionUtils.getOptionalRawType(parameter.getParameterizedType())); } private T getAnnotationFromParameterType(Parameter parameter, Class annotationClass) { final Optional> optionalRawType = ReflectionUtils.getOptionalRawType(parameter.getParameterizedType()); //will not work for type variable properties, which are bound to class that is annotated. return optionalRawType.map(aClass -> findAnnotation(collectAnnotations(aClass).getAnnotations(), annotationClass)) .orElse(null); } /** * Checks for {@link JsonbDeserializer} on a type. * * @param clsElement type not null * @return components info */ public DeserializerBinding getDeserializerBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); JsonbTypeDeserializer deserializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeDeserializer.class); if (deserializerAnnotation == null) { return null; } final Class deserializerClass = deserializerAnnotation.value(); return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null); } /** * Checks for {@link JsonbSerializer} on a property. * * @param property property not null * @return components info */ public SerializerBinding getSerializerBinding(Property property) { Objects.requireNonNull(property); JsonbTypeSerializer serializerAnnotation = getAnnotationFromProperty(JsonbTypeSerializer.class, property) .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeSerializer.class)); if (serializerAnnotation == null) { return null; } final Class serializerClass = serializerAnnotation.value(); return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null); } /** * Checks for {@link JsonbSerializer} on a type. * * @param clsElement type not null * @return components info */ public SerializerBinding getSerializerBinding(JsonbAnnotatedElement> clsElement) { Objects.requireNonNull(clsElement); JsonbTypeSerializer serializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeSerializer.class); if (serializerAnnotation == null) { return null; } final Class serializerClass = serializerAnnotation.value(); return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null); } private T getAnnotationFromPropertyType(Property property, Class annotationClass) { final Optional> optionalRawType = ReflectionUtils.getOptionalRawType(property.getPropertyType()); if (!optionalRawType.isPresent()) { //will not work for type variable properties, which are bound to class that is annotated. return null; } return findAnnotation(collectAnnotations(optionalRawType.get()).getAnnotations(), annotationClass); } /** * Checks if property is nillable. * Looks for {@link JsonbProperty} nillable attribute only. * JsonbNillable is checked only for ClassModels. * * @param property property to search in, not null * @return True if property should be serialized when null. */ public Optional isPropertyNillable(Property property) { Objects.requireNonNull(property); Optional nillable = getAnnotationFromProperty(JsonbNillable.class, property); if (nillable.isPresent()) { return nillable.map(JsonbNillable::value); } final Optional jsonbProperty = getAnnotationFromProperty(JsonbProperty.class, property); return jsonbProperty.map(JsonbProperty::nillable); } /** * Checks for JsonbNillable annotation on a class, its superclasses and interfaces. * * @param clazzElement class to search JsonbNillable in. * @return true if found */ public boolean isClassNillable(JsonbAnnotatedElement> clazzElement) { final JsonbNillable jsonbNillable = findAnnotation(clazzElement.getAnnotations(), JsonbNillable.class); if (jsonbNillable != null) { return jsonbNillable.value(); } Class clazz = clazzElement.getElement(); if (clazz == Optional.class || clazz == OptionalDouble.class || clazz == OptionalInt.class || clazz == OptionalLong.class) { return true; } return jsonbContext.getConfigProperties().getConfigNullable(); } /** * Checks for {@link JsonbPropertyOrder} annotation. * * @param clazzElement class to search on * @return ordered properties names or null if not found */ public String[] getPropertyOrder(JsonbAnnotatedElement> clazzElement) { final JsonbPropertyOrder jsonbPropertyOrder = clazzElement.getElement().getAnnotation(JsonbPropertyOrder.class); return jsonbPropertyOrder != null ? jsonbPropertyOrder.value() : null; } /** * Checks if property is annotated transient. If JsonbTransient annotation is present on field getter or setter, and other * annotation is present * on either of it, JsonbException is thrown with message describing collision. * * @param property The property to inspect if there is any {@link JsonbTransient} annotation defined for it * @return Set of {@link AnnotationTarget}s specifying in which scope the {@link JsonbTransient} is applied */ public EnumSet getJsonbTransientCategorized(Property property) { Objects.requireNonNull(property); EnumSet transientTarget = EnumSet.noneOf(AnnotationTarget.class); Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbTransient.class, property); if (annotationFromPropertyCategorized.size() > 0) { transientTarget.addAll(annotationFromPropertyCategorized.keySet()); return transientTarget; } return transientTarget; } /** * Search {@link JsonbDateFormat} on property, if not found looks at annotations declared on property type class. * * @param property Property to search on. * @return Map of {@link JsonbDateFormatter} instances categorized by their scopes (class, property, getter or setter). If * there is no date * formatter specified for given property, an empty map would be returned */ public Map getJsonbDateFormatCategorized(Property property) { Objects.requireNonNull(property); Map result = new HashMap<>(); Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbDateFormat.class, property); if (annotationFromPropertyCategorized.size() != 0) { annotationFromPropertyCategorized.forEach((key, annotation) -> result .put(key, createJsonbDateFormatter(annotation.value(), annotation.locale(), property))); } // No date format on property, try class level // if property is not TypeVariable and its class is not date skip it final Optional> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property.getPropertyType()); if (propertyRawTypeOptional.isPresent()) { Class rawType = propertyRawTypeOptional.get(); if (!( Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType) || TemporalAccessor.class.isAssignableFrom(rawType))) { return new HashMap<>(); } } JsonbDateFormat classLevelDateFormatter = findAnnotation(property.getDeclaringClassElement().getAnnotations(), JsonbDateFormat.class); if (classLevelDateFormatter != null) { result.put(AnnotationTarget.CLASS, createJsonbDateFormatter(classLevelDateFormatter.value(), classLevelDateFormatter.locale(), property)); } return result; } /** * Search for {@link JsonbDateFormat} annotation on java class and construct {@link JsonbDateFormatter}. * If not found looks at annotations declared on property type class. * * @param clazzElement class to search not null * @return formatter to use */ public JsonbDateFormatter getJsonbDateFormat(JsonbAnnotatedElement> clazzElement) { Objects.requireNonNull(clazzElement); final JsonbDateFormat format = findAnnotation(clazzElement.getAnnotations(), JsonbDateFormat.class); if (format == null) { return jsonbContext.getConfigProperties().getConfigDateFormatter(); } return new JsonbDateFormatter(format.value(), format.locale()); } /** * Search for {@link JsonbNumberFormat} annotation on java class. * * @param clazzElement class to search not null * @return formatter to use */ public JsonbNumberFormatter getJsonbNumberFormat(JsonbAnnotatedElement> clazzElement) { final JsonbNumberFormat formatAnnotation = findAnnotation(clazzElement.getAnnotations(), JsonbNumberFormat.class); if (formatAnnotation == null) { return null; } return new JsonbNumberFormatter(formatAnnotation.value(), formatAnnotation.locale()); } /** * Search {@link JsonbNumberFormat} on property, if not found looks at annotations declared on property type class. * * @param property Property to search on. * @return Map of {@link JsonbNumberFormatter} instances categorized by their scopes (class, property, getter or setter). * If there is no number * formatter specified for given property, an empty map would be returned */ public Map getJsonNumberFormatter(Property property) { Map result = new HashMap<>(); Map annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized( JsonbNumberFormat.class, property); // if (annotationFromPropertyCategorized.size() == 0) { // final Optional> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property // .getPropertyType()); // if (propertyRawTypeOptional.isPresent()) { // Class rawType = propertyRawTypeOptional.get(); // if (!Number.class.isAssignableFrom(rawType)) { // return new HashMap<>(); // } // } // } else { // annotationFromPropertyCategorized.forEach((key, annotation) -> result // .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale()))); // } annotationFromPropertyCategorized.forEach((key, annotation) -> result .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale()))); JsonbNumberFormat classLevelNumberFormatter = findAnnotation(property.getDeclaringClassElement().getAnnotations(), JsonbNumberFormat.class); if (classLevelNumberFormatter != null) { result.put(AnnotationTarget.CLASS, new JsonbNumberFormatter(classLevelNumberFormatter.value(), classLevelNumberFormatter.locale())); } return result; } /** * Returns {@link JsonbNumberFormatter} instance if {@link JsonbNumberFormat} annotation is present. * * @param param annotated method parameter * @return formatter instance if {@link JsonbNumberFormat} is present otherwise null */ public JsonbNumberFormatter getConstructorNumberFormatter(JsonbAnnotatedElement param) { return param.getAnnotation(JsonbNumberFormat.class) .map(annotation -> new JsonbNumberFormatter(annotation.value(), annotation.locale())) .orElse(null); } /** * Returns {@link JsonbDateFormatter} instance if {@link JsonbDateFormat} annotation is present. * * @param param annotated method parameter * @return formatter instance if {@link JsonbDateFormat} is present otherwise null */ public JsonbDateFormatter getConstructorDateFormatter(JsonbAnnotatedElement param) { return param.getAnnotation(JsonbDateFormat.class) .map(annotation -> new JsonbDateFormatter(DateTimeFormatter.ofPattern(annotation.value(), Locale.forLanguageTag(annotation.locale())), annotation.value(), annotation.locale())) .orElse(null); } /** * Creates {@link JsonbDateFormatter} caches formatter instance if possible. * For DEFAULT_FORMAT appropriate singleton instances from java.time.format.DateTimeFormatter * are used in date converters. */ private JsonbDateFormatter createJsonbDateFormatter(String format, String locale, Property property) { if (JsonbDateFormat.TIME_IN_MILLIS.equals(format) || JsonbDateFormat.DEFAULT_FORMAT.equals(format)) { //for epochMillis formatter is not used, for default format singleton instances of DateTimeFormatter //are used in the converters return new JsonbDateFormatter(format, locale); } final Optional> optionalRawType = ReflectionUtils.getOptionalRawType(property.getPropertyType()); final Class propertyRawType = optionalRawType.orElse(null); if (propertyRawType != null && !TemporalAccessor.class.isAssignableFrom(propertyRawType) && !Date.class.isAssignableFrom(propertyRawType) && !Calendar.class.isAssignableFrom(propertyRawType)) { throw new IllegalStateException(Messages.getMessage(MessageKeys.UNSUPPORTED_DATE_TYPE, propertyRawType)); } DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); builder.appendPattern(format); if (jsonbContext.getConfigProperties().isZeroTimeDefaulting()) { builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0); builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0); builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0); } DateTimeFormatter dateTimeFormatter = builder.toFormatter(Locale.forLanguageTag(locale)); return new JsonbDateFormatter(dateTimeFormatter, format, locale); } /** * Get a @JsonbVisibility annotation from a class or its package. * * @param clazz Class to lookup annotation * @return Instantiated PropertyVisibilityStrategy if annotation is present */ public PropertyVisibilityStrategy getPropertyVisibilityStrategy(Class clazz) { JsonbVisibility visibilityAnnotation = findAnnotation(clazz.getDeclaredAnnotations(), JsonbVisibility.class); if ((visibilityAnnotation == null) && (clazz.getPackage() != null)) { visibilityAnnotation = findAnnotation(clazz.getPackage().getDeclaredAnnotations(), JsonbVisibility.class); } if (visibilityAnnotation != null) { return ReflectionUtils.createNoArgConstructorInstance( ReflectionUtils.getDefaultConstructor(visibilityAnnotation.value(), true)); } return jsonbContext.getConfigProperties().getPropertyVisibilityStrategy(); } /** * Gets an annotation from first resolved annotation in a property in this order: *

1. Field, 2. Getter, 3 Setter.

* First found overrides other. * * @param annotationClass Annotation class to search for * @param property property to search in * @param Annotation type * @return Annotation if found, null otherwise */ private Optional getAnnotationFromProperty(Class annotationClass, Property property) { T fieldAnnotation = getFieldAnnotation(annotationClass, property.getFieldElement()); if (fieldAnnotation != null) { return Optional.of(fieldAnnotation); } T getterAnnotation = getMethodAnnotation(annotationClass, property.getGetterElement()); if (getterAnnotation != null) { return Optional.of(getterAnnotation); } T setterAnnotation = getMethodAnnotation(annotationClass, property.getSetterElement()); if (setterAnnotation != null) { return Optional.of(setterAnnotation); } return Optional.empty(); } /** * An override of {@link #getAnnotationFromProperty(Class, Property)} in which it returns the results as a map so that the * caller can decide which * one to be used for read/write operation. Some annotations should have different behaviours based on the scope that * they're applied on. * * @param annotationClass The annotation class to search * @param property The property to search in * @param Annotation type * @return A map of all occurrences of requested annotation for given property. Caller can determine based on * {@link AnnotationTarget} that given * annotation is specified on what level (Class, Property, Getter or Setter). If no annotation found for given property, an * empty map would be * returned */ private Map getAnnotationFromPropertyCategorized(Class annotationClass, Property property) { Map result = new HashMap<>(); T fieldAnnotation = getFieldAnnotation(annotationClass, property.getFieldElement()); if (fieldAnnotation != null) { result.put(AnnotationTarget.PROPERTY, fieldAnnotation); } T getterAnnotation = getMethodAnnotation(annotationClass, property.getGetterElement()); if (getterAnnotation != null) { result.put(AnnotationTarget.GETTER, getterAnnotation); } T setterAnnotation = getMethodAnnotation(annotationClass, property.getSetterElement()); if (setterAnnotation != null) { result.put(AnnotationTarget.SETTER, setterAnnotation); } return result; } private T getFieldAnnotation(Class annotationClass, JsonbAnnotatedElement fieldElement) { if (fieldElement == null) { return null; } return findAnnotation(fieldElement.getAnnotations(), annotationClass); } private T findAnnotation(Annotation[] declaredAnnotations, Class annotationClass) { return AnnotationFinder.findAnnotation(declaredAnnotations, annotationClass, new HashSet<>()); } /** * Finds annotations incompatible with {@link JsonbTransient} annotation. * * @param target target to check */ public void checkTransientIncompatible(JsonbAnnotatedElement target) { if (target == null) { return; } for (Class ann : TRANSIENT_INCOMPATIBLE) { Annotation annotation = findAnnotation(target.getAnnotations(), ann); if (annotation != null) { throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_TRANSIENT_WITH_OTHER_ANNOTATIONS)); } } } private T getMethodAnnotation(Class annotationClass, JsonbAnnotatedElement methodElement) { if (methodElement == null) { return null; } return findAnnotation(methodElement.getAnnotations(), annotationClass); } private void collectFromInterfaces(Class annotationClass, Class clazz, Map, T> collectedAnnotations) { for (Class interfaceClass : clazz.getInterfaces()) { T annotation = findAnnotation(interfaceClass.getDeclaredAnnotations(), annotationClass); if (annotation != null) { collectedAnnotations.put(interfaceClass, annotation); } collectFromInterfaces(annotationClass, interfaceClass, collectedAnnotations); } } /** * Get class interfaces recursively. * * @param cls Class to process. * @return A list of all class interfaces. */ public Set> collectInterfaces(Class cls) { Set> collected = new LinkedHashSet<>(); Queue> toScan = new LinkedList<>(Arrays.asList(cls.getInterfaces())); Class nextIfc; while ((nextIfc = toScan.poll()) != null) { collected.add(nextIfc); toScan.addAll(Arrays.asList(nextIfc.getInterfaces())); } return collected; } /** * Processes customizations. * * @param clsElement Element to process. * @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation, * if set and no {@code JsonbProperty} annotations are present. * @return Populated {@link ClassCustomization} instance. */ public ClassCustomization introspectCustomization(JsonbAnnotatedElement> clsElement, ClassCustomization parentCustomization, PropertyNamingStrategy propertyNamingStrategy) { return ClassCustomization.builder() .nillable(isClassNillable(clsElement)) .dateTimeFormatter(getJsonbDateFormat(clsElement)) .numberFormatter(getJsonbNumberFormat(clsElement)) .creator(getCreator(clsElement.getElement(), propertyNamingStrategy)) .propertyOrder(getPropertyOrder(clsElement)) .adapterBinding(getAdapterBinding(clsElement)) .serializerBinding(getSerializerBinding(clsElement)) .deserializerBinding(getDeserializerBinding(clsElement)) .propertyVisibilityStrategy(getPropertyVisibilityStrategy(clsElement.getElement())) .polymorphismConfig(getPolymorphismConfig(clsElement, parentCustomization)) .build(); } private TypeInheritanceConfiguration getPolymorphismConfig(JsonbAnnotatedElement> clsElement, ClassCustomization parentCustomization) { TypeInheritanceConfiguration parentPolyConfig = parentCustomization.getPolymorphismConfig(); LinkedList> annotations = clsElement.getAnnotations(JsonbTypeInfo.class); if (parentPolyConfig != null) { if (annotations.size() == 1 && annotations.getFirst().isInherited()) { throw new JsonbException("Cannot process type information from multiple sources! Sources: " + parentPolyConfig.getDefinedType().getName() + " and " + annotations.getFirst()); } else if (annotations.size() > 1) { throw new JsonbException("Cannot process type information from multiple sources! Sources: " + annotations); } else if (annotations.isEmpty()) { return TypeInheritanceConfiguration.builder().of(parentPolyConfig) .inherited(true) .build(); } } ListIterator> listIterator = annotations.listIterator(annotations.size()); while (listIterator.hasPrevious()) { AnnotationWrapper annotationWrapper = listIterator.previous(); JsonbTypeInfo annotation = (JsonbTypeInfo) annotationWrapper.getAnnotation(); TypeInheritanceConfiguration.Builder builder = TypeInheritanceConfiguration.builder(); builder.fieldName(annotation.key()) .inherited(annotationWrapper.isInherited()) .parentConfig(parentPolyConfig) .definedType(annotationWrapper.getDefinedType()); for (JsonbSubtype subType : annotation.value()) { if (!annotationWrapper.getDefinedType().isAssignableFrom(subType.type())) { throw new JsonbException("Defined alias type has to be child of the current type. JsonbSubType on the " + annotationWrapper.getDefinedType().getName() + " defines incorrect alias " + subType); } builder.alias(subType.type(), subType.alias()); } parentPolyConfig = builder.build(); } checkDuplicityPolymorphicPropertyNames(parentPolyConfig); return parentPolyConfig; } private void checkDuplicityPolymorphicPropertyNames(TypeInheritanceConfiguration typeInheritanceConfiguration) { if (typeInheritanceConfiguration == null) { return; } Map keyNames = new HashMap<>(); TypeInheritanceConfiguration current = typeInheritanceConfiguration; while (current != null) { String fieldName = current.getFieldName(); if (keyNames.containsKey(fieldName)) { TypeInheritanceConfiguration conflicting = keyNames.get(fieldName); throw new JsonbException("One polymorphic chain cannot have two conflicting property names. " + "Polymorphic type defined on the type " + conflicting.getDefinedType().getName() + " and " + current.getDefinedType().getName() + " have conflicting property name"); } keyNames.put(fieldName, current); current = current.getParentConfig(); } } /** * Returns class if {@link ImplementationClass} annotation is present. * * @param property annotated property * @return Class if {@link ImplementationClass} is present otherwise null */ public Class getImplementationClass(Property property) { Optional annotationFromProperty = getAnnotationFromProperty(ImplementationClass.class, property); return annotationFromProperty.>map(ImplementationClass::value).orElse(null); } /** * Collect annotations of given class, its interfaces and the package. * * @param clazz Class to process. * @return Element with class and annotations. */ public JsonbAnnotatedElement> collectAnnotations(Class clazz) { JsonbAnnotatedElement> classElement = new JsonbAnnotatedElement<>(clazz); if (BuiltInTypes.isKnownType(clazz)) { return classElement; } Map, LinkedList>> interfaceAnnotations = collectInterfaceAnnotations(clazz, clazz); for (LinkedList> wrappers : interfaceAnnotations.values()) { for (AnnotationWrapper wrapper : wrappers) { if (classElement.getAnnotation(wrapper.getAnnotation().annotationType()).isEmpty() || REPEATABLE.contains(wrapper.getAnnotation().annotationType())) { classElement.putAnnotationWrapper(wrapper); } } } if (!clazz.isPrimitive() && !clazz.isArray() && (clazz.getPackage() != null)) { addIfNotPresent(classElement, null, clazz.getPackage().getAnnotations()); } return classElement; } private Map, LinkedList>> collectInterfaceAnnotations(Class currentInterf, Class processed) { Map, LinkedList>> map = new HashMap<>(); if (!currentInterf.equals(processed)) { for (Annotation annotation : currentInterf.getDeclaredAnnotations()) { map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()) .add(new AnnotationWrapper<>(annotation, true, currentInterf)); } } Map, LinkedList>> parents = new HashMap<>(); for (Class parentInterf : currentInterf.getInterfaces()) { Map, LinkedList>> current = collectInterfaceAnnotations(parentInterf, processed); current.entrySet().stream() .filter(entry -> !parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey())) .peek(entry -> { if (parents.containsKey(entry.getKey())) { throw new JsonbException("Cannot process annotation " + entry.getKey().getName() + " from multiple " + "parallel sources"); } }) .forEach(entry -> { parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); }); } return map; } // private void collectParentInterfaceAnnotations(Class currentInterf, // Map, LinkedList> overall) { // Map, LinkedList> parents = new HashMap<>(); // for (Class parentInterf : currentInterf.getInterfaces()) { // collectParentInterfaceAnnotations(parentInterf, ); // current.entrySet().stream() // .filter(entry -> parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey())) // .peek(entry -> { // if (parents.containsKey(entry.getKey())) { // throw new JsonbException("CHANGE THIS EXCEPTION"); // } // }) // .forEach(entry -> { // parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); // map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue()); // }); // } // if (currentInterf.isInterface()) { // for (Annotation annotation : currentInterf.getDeclaredAnnotations()) { // map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()).add(annotation); // } // } // return map; // } private void addIfNotPresent(JsonbAnnotatedElement element, Class definedType, Annotation... annotations) { for (Annotation annotation : annotations) { if (element.getAnnotation(annotation.annotationType()).isEmpty() || REPEATABLE.contains(annotation.annotationType())) { element.putAnnotation(annotation, true, definedType); } } } public boolean requiredParameters(Executable executable, JsonbAnnotatedElement annotated) { return jsonbContext.getConfigProperties().hasRequiredCreatorParameters(); // if (OPTIONALS.contains(annotated.getElement().getType())) { // return false; // } // return annotated.getAnnotation(JsonbRequired.class) // .or(() -> Optional.ofNullable(executable.getAnnotation(JsonbRequired.class))) // .map(JsonbRequired::value) // .orElseGet(() -> jsonbContext.getConfigProperties().hasRequiredCreatorParameters()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/BuiltInTypes.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; import java.util.TimeZone; import java.util.UUID; import javax.xml.datatype.XMLGregorianCalendar; import jakarta.json.JsonValue; /** * Types which are supported by the Yasson by default. */ public class BuiltInTypes { private static final Set> BUILD_IN_SUPPORT; static { Set> buildInTypes = new HashSet<>(); buildInTypes.add(Byte.class); buildInTypes.add(Byte.TYPE); buildInTypes.add(BigDecimal.class); buildInTypes.add(BigInteger.class); buildInTypes.add(Boolean.class); buildInTypes.add(Boolean.TYPE); buildInTypes.add(Calendar.class); buildInTypes.add(Character.class); buildInTypes.add(Character.TYPE); buildInTypes.add(Date.class); buildInTypes.add(Double.class); buildInTypes.add(Double.TYPE); buildInTypes.add(Duration.class); buildInTypes.add(Float.class); buildInTypes.add(Float.TYPE); buildInTypes.add(Integer.class); buildInTypes.add(Integer.TYPE); buildInTypes.add(Instant.class); buildInTypes.add(LocalDateTime.class); buildInTypes.add(LocalDate.class); buildInTypes.add(LocalTime.class); buildInTypes.add(Long.class); buildInTypes.add(Long.TYPE); buildInTypes.add(Number.class); buildInTypes.add(OffsetDateTime.class); buildInTypes.add(OffsetTime.class); buildInTypes.add(OptionalDouble.class); buildInTypes.add(OptionalInt.class); buildInTypes.add(OptionalLong.class); buildInTypes.add(Path.class); buildInTypes.add(Period.class); buildInTypes.add(Short.class); buildInTypes.add(Short.TYPE); buildInTypes.add(String.class); buildInTypes.add(TimeZone.class); buildInTypes.add(URI.class); buildInTypes.add(URL.class); buildInTypes.add(UUID.class); if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) { buildInTypes.add(XMLGregorianCalendar.class); } buildInTypes.add(ZonedDateTime.class); buildInTypes.add(ZoneId.class); buildInTypes.add(ZoneOffset.class); if (isClassAvailable("java.sql.Date")) { buildInTypes.add(java.sql.Date.class); buildInTypes.add(java.sql.Timestamp.class); } BUILD_IN_SUPPORT = Set.copyOf(buildInTypes); } private BuiltInTypes() { throw new IllegalStateException("Util class cannot be instantiated"); } /** * Check whether the class is available. * * @param className name of the checked class * @return true if available, otherwise false */ public static boolean isClassAvailable(String className) { try { Class.forName(className); return true; } catch (ClassNotFoundException | LinkageError e) { return false; } } /** * Whether the type is a supported type by default. * * @param clazz type to check * @return whether is supported */ public static boolean isKnownType(Class clazz) { boolean knownContainerValueType = Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || JsonValue.class.isAssignableFrom(clazz) || Optional.class.isAssignableFrom(clazz) || clazz.isArray(); return knownContainerValueType || findIfClassIsSupported(clazz); } private static boolean findIfClassIsSupported(Class clazz) { Class current = clazz; do { if (BUILD_IN_SUPPORT.contains(current)) { return true; } current = current.getSuperclass(); } while (current != null); return false; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java ================================================ /* * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Map; import java.util.Optional; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyNamingStrategy; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.model.Property; /** * Search for instance creator from other sources. * Mainly intended to add extensibility for different java versions and new features. */ public class ClassMultiReleaseExtension { private ClassMultiReleaseExtension() { throw new IllegalStateException("This class cannot be instantiated"); } static boolean shouldTransformToPropertyName(Method method) { return true; } static boolean isSpecialAccessorMethod(Method method, Map classProperties) { return false; } static JsonbCreator findCreator(Class clazz, Constructor[] declaredConstructors, AnnotationIntrospector introspector, PropertyNamingStrategy propertyNamingStrategy) { return null; } public static boolean isRecord(Class clazz) { return false; } public static Optional exceptionToThrow(Class clazz) { return Optional.empty(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ClassParser.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyVisibilityStrategy; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbAnnotatedElement; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.model.Property; import org.eclipse.yasson.internal.model.PropertyModel; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Created a class internal model. */ class ClassParser { private static final String IS_PREFIX = "is"; private static final String GET_PREFIX = "get"; private static final String SET_PREFIX = "set"; private final JsonbContext jsonbContext; ClassParser(JsonbContext jsonbContext) { this.jsonbContext = jsonbContext; } /** * Parse class fields and getters setters. Merge to java bean like properties. */ void parseProperties(ClassModel classModel, JsonbAnnotatedElement> classElement) { final Map classProperties = new HashMap<>(); parseFields(classElement, classProperties); parseClassAndInterfaceMethods(classElement, classProperties); //add sorted properties from parent, if they are not overridden in current class //parent properties are by default first by alphabet, than properties from a subclass final List sortedParentProperties = getSortedParentProperties(classModel, classElement, classProperties); List classPropertyModels = classProperties.values().stream() .map(property -> new PropertyModel(classModel, property, jsonbContext)) .collect(Collectors.toList()); //check for collision on same property read name List unsortedMerged = new ArrayList<>(sortedParentProperties.size() + classPropertyModels.size()); unsortedMerged.addAll(sortedParentProperties); unsortedMerged.addAll(classPropertyModels); checkPropertyNameClash(unsortedMerged, classModel.getType()); mergePropertyModels(classPropertyModels); List sortedPropertyModels = new ArrayList<>(sortedParentProperties.size() + classPropertyModels.size()); sortedPropertyModels.addAll(sortedParentProperties); sortedPropertyModels.addAll(jsonbContext.getConfigProperties().getPropertyOrdering() .orderProperties(classPropertyModels, classModel)); //reference property to creator parameter by name to merge configuration in runtime JsonbCreator creator = classModel.getClassCustomization().getCreator(); if (creator != null) { sortedPropertyModels.forEach(propertyModel -> { for (CreatorModel creatorModel : creator.getParams()) { if (creatorModel.getName().equals(propertyModel.getPropertyName())) { creatorModel.getCustomization().setPropertyModel(propertyModel); } } }); } classModel.setProperties(sortedPropertyModels); } private static void mergePropertyModels(List unsortedMerged) { PropertyModel[] clone = unsortedMerged.toArray(new PropertyModel[0]); for (int i = 0; i < clone.length; i++) { for (int j = i + 1; j < clone.length; j++) { PropertyModel firstPropertyModel = clone[i]; PropertyModel secondPropertyModel = clone[j]; if (firstPropertyModel.equals(secondPropertyModel)) { // Need to merge two properties unsortedMerged.remove(firstPropertyModel); unsortedMerged.remove(secondPropertyModel); if (!firstPropertyModel.isReadable() && !firstPropertyModel.isWritable()) { unsortedMerged.add(secondPropertyModel); } else if (!secondPropertyModel.isReadable() && !secondPropertyModel.isWritable()) { unsortedMerged.add(firstPropertyModel); } else { unsortedMerged.add(new PropertyModel(firstPropertyModel, secondPropertyModel)); } } } } } private void parseClassAndInterfaceMethods(JsonbAnnotatedElement> classElement, Map classProperties) { Class concreteClass = classElement.getElement(); parseMethods(concreteClass, classElement, classProperties); for (Class ifc : jsonbContext.getAnnotationIntrospector().collectInterfaces(concreteClass)) { parseIfaceMethodAnnotations(ifc, classElement, classProperties); } } private void parseIfaceMethodAnnotations(Class ifc, JsonbAnnotatedElement> classElement, Map classProperties) { Method[] declaredMethods = AccessController.doPrivileged((PrivilegedAction) ifc::getDeclaredMethods); for (Method method : declaredMethods) { final String methodName = method.getName(); if (!isPropertyMethod(method)) { continue; } String propertyName = toPropertyMethod(methodName); Property property = classProperties.get(propertyName); if (method.isDefault()) { // Interface provides default implementation if (property == null) { // the property does not yet exists : create it from scratch property = registerMethod(propertyName, method, classElement, classProperties); } else { // property already exists, take care not overriding already parsed implementation if (isSetter(method)) { if (property.getSetter() == null) { property.setSetter(method); } } else { if (property.getGetter() == null) { property.setGetter(method); } } } } if (property == null) { //May happen for classes which both extend a class with some method and implement interface with same method. continue; } JsonbAnnotatedElement methodElement = isGetter(method) ? property.getGetterElement() : property.getSetterElement(); //Only push iface annotations if not overridden on impl classes for (Annotation ann : method.getDeclaredAnnotations()) { if (methodElement.getAnnotation(ann.annotationType()).isEmpty()) { methodElement.putAnnotation(ann, true, null); } } } } private Property registerMethod(String propertyName, Method method, JsonbAnnotatedElement> classElement, Map classProperties) { Property property = classProperties.computeIfAbsent(propertyName, n -> new Property(n, classElement)); if (isSetter(method)) { property.setSetter(method); } else { property.setGetter(method); } return property; } private void parseMethods(Class clazz, JsonbAnnotatedElement> classElement, Map classProperties) { Method[] declaredMethods = AccessController.doPrivileged((PrivilegedAction) clazz::getDeclaredMethods); for (Method method : declaredMethods) { String name = method.getName(); //isBridge method filters out methods inherited from interfaces boolean isAccessorMethod = ClassMultiReleaseExtension.isSpecialAccessorMethod(method, classProperties) || isPropertyMethod(method); if (!isAccessorMethod || method.isBridge() || isSpecialCaseMethod(clazz, method)) { continue; } final String propertyName = ClassMultiReleaseExtension.shouldTransformToPropertyName(method) ? toPropertyMethod(name) : name; registerMethod(propertyName, method, classElement, classProperties); } } /** * Filter out certain methods that get forcibly added to some classes. * For example the public groovy.lang.MetaClass X.getMetaClass() method from Groovy classes */ private static boolean isSpecialCaseMethod(Class clazz, Method m) { if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers()) || m.isSynthetic()) { return false; } // Groovy objects will have public groovy.lang.MetaClass X.getMetaClass() // which causes an infinite loop in serialization if (m.getName().equals("getMetaClass") && m.getReturnType().getCanonicalName().equals("groovy.lang.MetaClass")) { return true; } // WELD proxy objects will have 'public org.jboss.weld if (m.getName().equals("getMetadata") && m.getReturnType().getCanonicalName().equals("org.jboss.weld.proxy.WeldClientProxy$Metadata")) { return true; } return false; } private static boolean isGetter(Method m) { return (m.getName().startsWith(GET_PREFIX) || m.getName().startsWith(IS_PREFIX)) && m.getParameterCount() == 0; } private static boolean isSetter(Method m) { return m.getName().startsWith(SET_PREFIX) && m.getParameterCount() == 1; } private static String toPropertyMethod(String name) { return lowerFirstLetter(name.substring(name.startsWith(IS_PREFIX) ? 2 : 3)); } private static String lowerFirstLetter(String name) { Objects.requireNonNull(name); if (name.length() == 0) { //methods named get() or set() return name; } if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) { return name; } char[] chars = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } private static boolean isPropertyMethod(Method m) { return isGetter(m) || isSetter(m); } private static void parseFields(JsonbAnnotatedElement> classElement, Map classProperties) { Field[] declaredFields = AccessController.doPrivileged( (PrivilegedAction) () -> classElement.getElement().getDeclaredFields()); for (Field field : declaredFields) { final String name = field.getName(); if (field.isSynthetic()) { continue; } final Property property = new Property(name, classElement); property.setField(field); classProperties.put(name, property); } } private static void checkPropertyNameClash(List collectedProperties, Class cls) { final List checkedProperties = new ArrayList<>(); for (PropertyModel collectedPropertyModel : collectedProperties) { for (PropertyModel checkedPropertyModel : checkedProperties) { if ((checkedPropertyModel.getReadName().equals(collectedPropertyModel.getReadName()) && checkedPropertyModel.isReadable() // && collectedPropertyModel.isReadable()) || (checkedPropertyModel.getWriteName().equals(collectedPropertyModel.getWriteName()) && checkedPropertyModel.isWritable() // && collectedPropertyModel.isWritable())) { throw new JsonbException( Messages.getMessage(MessageKeys.PROPERTY_NAME_CLASH, checkedPropertyModel.getPropertyName(), collectedPropertyModel.getPropertyName(), cls.getName())); } } checkedProperties.add(collectedPropertyModel); } } /** * Merges current class properties with parent class properties. * If javabean property is declared in more than one inheritance levels, * merge field, getters and setters of that property. *

* For example BaseClass contains field foo and getter getFoo. In BaseExtensions there is a setter setFoo. * All three will be merged for BaseExtension. *

* Such property is sorted based on where its getter or field is located. */ private List getSortedParentProperties(ClassModel classModel, JsonbAnnotatedElement> classElement, Map classProperties) { List sortedProperties = new ArrayList<>(); //Pull properties from parent if (classModel.getParentClassModel() != null) { for (PropertyModel parentProp : classModel.getParentClassModel().getSortedProperties()) { final Property current = classProperties.get(parentProp.getPropertyName()); //don't replace overridden properties if (current == null) { sortedProperties.add(parentProp); } else { //merge final Property merged = mergeProperty(current, parentProp, classElement); PropertyVisibilityStrategy propertyVisibilityStrategy = classModel.getClassCustomization() .getPropertyVisibilityStrategy(); if (PropertyModel.isPropertyReadable(current.getField(), current.getGetter(), propertyVisibilityStrategy)) { classProperties.replace(current.getName(), merged); } else { sortedProperties.add(new PropertyModel(classModel, merged, jsonbContext)); classProperties.remove(current.getName()); } } } } return sortedProperties; } /** * Select the correct method to use. The correct method is the most specific * method which is not a default one: *

    *
  • if current is not defined, returns parent;
  • *
  • if parent is not defined, returns current;
  • *
  • if current is a default method and parent is not, returns parent;
  • *
      *
    • By definition, it is not possible to make a choice betweentwo default * methods.
      Here, the most specific is selected, but a concrete * implementation MUST eventually be provided as the source code won't even * compile if such a method does not exist
    • *
    *
  • returns current otherwise
  • *
* * @param current current 'child' implementation * @param parent parent implementation * @return effective method to register as getter or setter */ private static Method selectMostSpecificNonDefaultMethod(Method current, Method parent) { return ( current != null ? ( parent != null && current.isDefault() && !parent.isDefault() ? parent : current) : parent); } private static Property mergeProperty(Property current, PropertyModel parentProp, JsonbAnnotatedElement> classElement) { Field field = current.getField() != null ? current.getField() : parentProp.getField(); Method getter = selectMostSpecificNonDefaultMethod(current.getGetter(), parentProp.getGetter()); Method setter = selectMostSpecificNonDefaultMethod(current.getSetter(), parentProp.getSetter()); Property merged = new Property(parentProp.getPropertyName(), classElement); if (field != null) { merged.setField(field); } if (getter != null) { merged.setGetter(getter); } if (setter != null) { merged.setSetter(setter); } return merged; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.LinkedList; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.bind.annotation.JsonbTypeSerializer; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import org.eclipse.yasson.internal.components.AbstractComponentBinding; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.ComponentBindings; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.components.SerializerBinding; import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization; /** * Searches for a registered components or Serializer for a given type. */ public class ComponentMatcher { private final JsonbContext jsonbContext; /** * Flag for searching for generic serializers and adapters in runtime. */ private volatile boolean genericComponents; private final ConcurrentMap userComponents; /** * Create component matcher. * * @param context mandatory */ ComponentMatcher(JsonbContext context) { Objects.requireNonNull(context); this.jsonbContext = context; userComponents = new ConcurrentHashMap<>(); init(); } /** * Called during context creation, introspecting user components provided with JsonbConfig. */ void init() { final JsonbSerializer[] serializers = (JsonbSerializer[]) jsonbContext.getConfig() .getProperty(JsonbConfig.SERIALIZERS).orElseGet(() -> new JsonbSerializer[] {}); for (JsonbSerializer serializer : serializers) { SerializerBinding serializerBinding = introspectSerializerBinding(serializer.getClass(), serializer); addSerializer(serializerBinding.getBindingType(), serializerBinding); } final JsonbDeserializer[] deserializers = (JsonbDeserializer[]) jsonbContext.getConfig() .getProperty(JsonbConfig.DESERIALIZERS).orElseGet(() -> new JsonbDeserializer[] {}); for (JsonbDeserializer deserializer : deserializers) { DeserializerBinding deserializerBinding = introspectDeserializerBinding(deserializer.getClass(), deserializer); addDeserializer(deserializerBinding.getBindingType(), deserializerBinding); } final JsonbAdapter[] adapters = (JsonbAdapter[]) jsonbContext.getConfig().getProperty(JsonbConfig.ADAPTERS) .orElseGet(() -> new JsonbAdapter[] {}); for (JsonbAdapter adapter : adapters) { AdapterBinding adapterBinding = introspectAdapterBinding(adapter.getClass(), adapter); addAdapter(adapterBinding.getBindingType(), adapterBinding); } } private ComponentBindings getBindingInfo(Type type) { return userComponents .compute(type, (type1, bindingInfo) -> bindingInfo != null ? bindingInfo : new ComponentBindings(type1)); } private void addSerializer(Type bindingType, SerializerBinding serializer) { userComponents.computeIfPresent(bindingType, (type, bindings) -> { if (bindings.getSerializer() != null) { return bindings; } registerGeneric(bindingType); return new ComponentBindings(bindingType, serializer, bindings.getDeserializer(), bindings.getAdapterInfo()); }); } private void addDeserializer(Type bindingType, DeserializerBinding deserializer) { userComponents.computeIfPresent(bindingType, (type, bindings) -> { if (bindings.getDeserializer() != null) { return bindings; } registerGeneric(bindingType); return new ComponentBindings(bindingType, bindings.getSerializer(), deserializer, bindings.getAdapterInfo()); }); } private void addAdapter(Type bindingType, AdapterBinding adapter) { userComponents.computeIfPresent(bindingType, (type, bindings) -> { if (bindings.getAdapterInfo() != null) { return bindings; } registerGeneric(bindingType); return new ComponentBindings(bindingType, bindings.getSerializer(), bindings.getDeserializer(), adapter); }); } /** * If type is not parametrized runtime component resolution doesn't has to happen. * * @param bindingType component binding type */ private void registerGeneric(Type bindingType) { if (bindingType instanceof ParameterizedType && !genericComponents) { genericComponents = true; } } /** * Lookup serializer binding for a given property runtime type. * * @param propertyRuntimeType runtime type of a property * @param customization with component info * @return serializer optional */ public Optional> getSerializerBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getSerializerBinding() == null) { return searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializer, this::getAnnotationBasedSerializer); } final SerializerBinding binding = customization.getSerializerBinding(); // If the binding type exactly matches the runtime type, use it (optimization) if (binding.getBindingType().equals(propertyRuntimeType)) { return Optional.of(binding); } // Special handling for Object type: search for more specific serializers based on runtime type // This allows annotation-based or config-based serializers on concrete types to be found // when the property is declared as Object but has a specific runtime type if (Object.class.equals(binding.getBindingType())) { final Optional> moreSpecific = searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializer, this::getAnnotationBasedSerializer); if (moreSpecific.isPresent()) { return moreSpecific; } } // Use the customization binding (user explicitly configured it for this property) return Optional.of(binding); } /** * Lookup deserializer binding for a given property runtime type. * * @param propertyRuntimeType runtime type of a property * @param customization customization with component info * @return serializer optional */ public Optional> getDeserializerBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getDeserializerBinding() == null) { return searchComponentBinding(propertyRuntimeType, ComponentBindings::getDeserializer); } return Optional.of(customization.getDeserializerBinding()); } /** * Get components from property model (if declared by annotation and runtime type matches), * or return components searched by runtime type. * * @param propertyRuntimeType runtime type not null * @param customization customization with component info * @return components info if present */ public Optional getSerializeAdapterBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getSerializeAdapterBinding() == null) { return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo); } // Check if the customization's adapter binding matches the runtime type AdapterBinding binding = customization.getSerializeAdapterBinding(); if (matches(propertyRuntimeType, binding.getBindingType())) { return Optional.of(binding); } // The annotation-based adapter doesn't match the runtime type, // fall through to search for a better match based on runtime type return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo); } /** * Get components from property model (if declared by annotation and runtime type matches), * or return components searched by runtime type. * * @param propertyRuntimeType runtime type not null * @param customization customization with component info * @return components info if present */ public Optional getDeserializeAdapterBinding(Type propertyRuntimeType, ComponentBoundCustomization customization) { if (customization == null || customization.getDeserializeAdapterBinding() == null) { return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo); } return Optional.of(customization.getDeserializeAdapterBinding()); } /** * Search for a component binding for the given runtime type. * * @param runtimeType The runtime type to find a component for * @param supplier Function to extract the desired component from ComponentBindings * @param annotationDiscovery Optional function for runtime annotation discovery (null if not applicable) * @param The type of component binding to search for * @return Optional containing the component binding if found */ private Optional searchComponentBinding( Type runtimeType, Function supplier, Function, Optional> annotationDiscovery) { // First check if there is an exact match ComponentBindings binding = userComponents.get(runtimeType); if (binding != null) { Optional match = getMatchingBinding(runtimeType, binding, supplier); if (match.isPresent()) { return match; } } Optional> runtimeClass = ReflectionUtils.getOptionalRawType(runtimeType); if (runtimeClass.isPresent()) { // Check for annotation-based component on the runtime type itself // Currently only used for @JsonbTypeSerializer during serialization if (annotationDiscovery != null) { Optional annotationBased = annotationDiscovery.apply(runtimeClass.get()); if (annotationBased.isPresent()) { return annotationBased; } } // Check if any interfaces have a match for (Class ifc : runtimeClass.get().getInterfaces()) { ComponentBindings ifcBinding = userComponents.get(ifc); if (ifcBinding != null) { Optional match = getMatchingBinding(ifc, ifcBinding, supplier); if (match.isPresent()) { return match; } } } // check if the superclass has a match Class superClass = runtimeClass.get().getSuperclass(); if (superClass != null && superClass != Object.class) { Optional superBinding = searchComponentBinding(superClass, supplier, annotationDiscovery); if (superBinding.isPresent()) { return superBinding; } } } return Optional.empty(); } // Convenience overload for components without annotation discovery (deserializers, adapters) private Optional searchComponentBinding( final Type runtimeType, final Function supplier) { return searchComponentBinding(runtimeType, supplier, null); } /** * Discovers and caches a serializer defined by @JsonbTypeSerializer annotation on a runtime type. * *

This method performs runtime annotation discovery during serialization, * which is distinct from the build-time annotation introspection performed by AnnotationIntrospector. * It is invoked when serializing a property where the runtime type is more specific than the * declared type (e.g., a property declared as {@code Object} containing an instance of a class * annotated with @JsonbTypeSerializer).

* *

Note: Only @JsonbTypeSerializer is checked, not @JsonbTypeAdapter or @JsonbTypeDeserializer, * because:

*
    *
  • Serializers are unidirectional (serialization only), so runtime discovery is complete
  • *
  • Deserializers don't apply - we lack runtime type information during deserialization
  • *
  • Adapters are bidirectional - discovering them only at runtime during serialization * would be incomplete since they couldn't be discovered during deserialization
  • *
* * @param clazz The runtime class to check for @JsonbTypeSerializer annotation * @return SerializerBinding if annotation is present and successfully introspected, empty otherwise */ private Optional> getAnnotationBasedSerializer(final Class clazz) { // Check if the class has a @JsonbTypeSerializer annotation final JsonbTypeSerializer annotation = clazz.getAnnotation(JsonbTypeSerializer.class); if (annotation == null) { return Optional.empty(); } // Thread-safe get-or-create using compute final SerializerBinding binding = userComponents.compute(clazz, (type, bindings) -> { // If already cached, return as-is if (bindings != null && bindings.getSerializer() != null) { return bindings; } // Create new serializer binding final Class serializerClass = annotation.value(); final JsonbSerializer serializer = jsonbContext.getComponentInstanceCreator().getOrCreateComponent(serializerClass); final SerializerBinding newBinding = new SerializerBinding<>(clazz, serializer); // Create or update ComponentBindings if (bindings == null) { return new ComponentBindings(clazz, newBinding, null, null); } return new ComponentBindings(clazz, newBinding, bindings.getDeserializer(), bindings.getAdapterInfo()); }).getSerializer(); return Optional.ofNullable(binding); } private Optional getMatchingBinding(Type runtimeType, ComponentBindings binding, Function supplier) { final T component = supplier.apply(binding); if (component != null && matches(runtimeType, binding.getBindingType())) { return Optional.of(component); } return Optional.empty(); } private boolean matches(Type runtimeType, Type componentBindingType) { if (componentBindingType.equals(runtimeType)) { return true; } if (componentBindingType instanceof Class && runtimeType instanceof Class) { return ((Class) componentBindingType).isAssignableFrom((Class) runtimeType); } //don't try to runtime generic scan if not needed if (!genericComponents) { return false; } return runtimeType instanceof ParameterizedType && componentBindingType instanceof ParameterizedType && ReflectionUtils.getRawType(componentBindingType).isAssignableFrom(ReflectionUtils.getRawType(runtimeType)) && matchTypeArguments((ParameterizedType) runtimeType, (ParameterizedType) componentBindingType); } /** * If runtimeType to adapt is a ParametrizedType, check all type args to match against components args. */ private boolean matchTypeArguments(ParameterizedType requiredType, ParameterizedType componentBound) { final Type[] requiredTypeArguments = requiredType.getActualTypeArguments(); final Type[] adapterBoundTypeArguments = componentBound.getActualTypeArguments(); if (requiredTypeArguments.length != adapterBoundTypeArguments.length) { return false; } for (int i = 0; i < requiredTypeArguments.length; i++) { Type adapterTypeArgument = adapterBoundTypeArguments[i]; if (!requiredTypeArguments[i].equals(adapterTypeArgument)) { return false; } } return true; } /** * Introspect components generic information and put resolved types into metadata wrapper. * * @param adapterClass class of an components * @param instance components instance * @return introspected info with resolved typevar types. */ AdapterBinding introspectAdapterBinding(Class adapterClass, JsonbAdapter instance) { final ParameterizedType adapterRuntimeType = ReflectionUtils.findParameterizedType(adapterClass, JsonbAdapter.class); final Type[] adapterTypeArguments = adapterRuntimeType.getActualTypeArguments(); Type adaptFromType = resolveTypeArg(adapterTypeArguments[0], adapterClass); Type adaptToType = resolveTypeArg(adapterTypeArguments[1], adapterClass); final ComponentBindings componentBindings = getBindingInfo(adaptFromType); if (componentBindings.getAdapterInfo() != null && componentBindings.getAdapterInfo().getAdapter().getClass() .equals(adapterClass)) { return componentBindings.getAdapterInfo(); } JsonbAdapter newAdapter = instance != null ? instance : jsonbContext.getComponentInstanceCreator().getOrCreateComponent(adapterClass); return new AdapterBinding(adaptFromType, adaptToType, newAdapter); } /** * If an instance of deserializerClass is present in context and is bound for same type, return that instance. * Otherwise create new instance and set it to context. * * @param deserializerClass class of deserializer * @param instance instance to use if not cached already * @return wrapper used in property models */ @SuppressWarnings("unchecked") DeserializerBinding introspectDeserializerBinding(Class deserializerClass, JsonbDeserializer instance) { final ParameterizedType deserializerRuntimeType = ReflectionUtils .findParameterizedType(deserializerClass, JsonbDeserializer.class); Type deserializerBindingType = resolveTypeArg(deserializerRuntimeType.getActualTypeArguments()[0], deserializerClass); final ComponentBindings componentBindings = getBindingInfo(deserializerBindingType); if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getClass() .equals(deserializerClass)) { return componentBindings.getDeserializer(); } else { JsonbDeserializer deserializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator() .getOrCreateComponent(deserializerClass); return new DeserializerBinding(deserializerBindingType, deserializer); } } /** * If an instance of serializerClass is present in context and is bound for same type, return that instance. * Otherwise create new instance and set it to context. * * @param serializerClass class of deserializer * @param instance instance to use if not cached * @return wrapper used in property models */ @SuppressWarnings("unchecked") SerializerBinding introspectSerializerBinding(Class serializerClass, JsonbSerializer instance) { final ParameterizedType serializerRuntimeType = ReflectionUtils .findParameterizedType(serializerClass, JsonbSerializer.class); Type serBindingType = resolveTypeArg(serializerRuntimeType.getActualTypeArguments()[0], serializerClass); final ComponentBindings componentBindings = getBindingInfo(serBindingType); if (componentBindings.getSerializer() != null && componentBindings.getSerializer().getClass().equals(serializerClass)) { return componentBindings.getSerializer(); } else { JsonbSerializer serializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator() .getOrCreateComponent(serializerClass); return new SerializerBinding(serBindingType, serializer); } } private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) { if (adapterTypeArg instanceof ParameterizedType) { return ReflectionUtils.resolveTypeArguments((ParameterizedType) adapterTypeArg, adapterType); } else if (adapterTypeArg instanceof TypeVariable) { LinkedList chain = new LinkedList<>(); chain.add(adapterType); return ReflectionUtils.resolveItemVariableType(chain, (TypeVariable) adapterTypeArg, true); } else { return adapterTypeArg; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.logging.Logger; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; class ConstructorPropertiesAnnotationIntrospector { private static final Logger LOG = Logger.getLogger(ConstructorPropertiesAnnotationIntrospector.class.getName()); private final JsonbContext jsonbContext; private final AnnotationFinder constructorProperties; public static ConstructorPropertiesAnnotationIntrospector forContext(JsonbContext jsonbContext) { return new ConstructorPropertiesAnnotationIntrospector(jsonbContext, AnnotationFinder.findConstructorProperties()); } /** * Only for testing and internal purposes. *

* Please use static factory methods e.g. {@link #forContext(JsonbContext)}. * * @param context {@link JsonbContext} * @param annotationFinder {@link AnnotationFinder} */ protected ConstructorPropertiesAnnotationIntrospector(JsonbContext context, AnnotationFinder annotationFinder) { this.jsonbContext = context; this.constructorProperties = annotationFinder; } public JsonbCreator getCreator(Constructor[] constructors) { JsonbCreator jsonbCreator = null; for (Constructor constructor : constructors) { Object properties = constructorProperties.valueIn(constructor.getDeclaredAnnotations()); if (!(properties instanceof String[])) { continue; } if (!Modifier.isPublic(constructor.getModifiers())) { String declaringClass = constructor.getDeclaringClass().getName(); String message = "The constructor of {0} annotated with @ConstructorProperties {1} is not accessible and will " + "be ignored."; LOG.finest(String.format(message, declaringClass, Arrays.toString((String[]) properties))); continue; } if (jsonbCreator != null) { // don't fail in this case, because it is perfectly allowed to have more than one // @ConstructorProperties-Annotation in general. // It is just undefined, which constructor to choose for JSON in this case. // The behavior should be the same (null), as if there is no ConstructorProperties-Annotation at all. LOG.warning(Messages.getMessage(MessageKeys.MULTIPLE_CONSTRUCTOR_PROPERTIES_CREATORS, constructor.getDeclaringClass().getName())); return null; } jsonbCreator = createJsonbCreator(constructor, (String[]) properties); } return jsonbCreator; } private JsonbCreator createJsonbCreator(Executable executable, String[] properties) { final Parameter[] parameters = executable.getParameters(); CreatorModel[] creatorModels = new CreatorModel[parameters.length]; for (int i = 0; i < parameters.length; i++) { final Parameter parameter = parameters[i]; creatorModels[i] = new CreatorModel(properties[i], parameter, executable, jsonbContext); } return new JsonbCreator(executable, creatorModels); } @Override public String toString() { return "ConstructorPropertiesAnnotationIntrospector [jsonbContext=" + jsonbContext + ", constructorProperties=" + constructorProperties + "]"; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/DeserializationContextImpl.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import jakarta.json.bind.JsonbException; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserialization context implementation. */ public class DeserializationContextImpl extends ProcessingContext implements DeserializationContext { private final List delayedSetters = new ArrayList<>(); private JsonParser.Event lastValueEvent; private Customization customization = ClassCustomization.empty(); private Object instance; /** * Parent instance for marshaller and unmarshaller. * * @param jsonbContext context of Jsonb */ public DeserializationContextImpl(JsonbContext jsonbContext) { super(jsonbContext); } /** * Create new instance based on previous context. * * @param context previous deserialization context */ public DeserializationContextImpl(DeserializationContextImpl context) { super(context.getJsonbContext()); this.lastValueEvent = context.lastValueEvent; } /** * Return instance of currently deserialized type. * * @return null if instance has not been created yet */ public Object getInstance() { return instance; } /** * Set currently deserialized type instance. * * @param instance deserialized type instance */ public void setInstance(Object instance) { this.instance = instance; } /** * Return the list of deferred deserializers. * * @return list of deferred deserializers */ public List getDeferredDeserializers() { return delayedSetters; } /** * Return last obtained {@link JsonParser.Event} event. * * @return last obtained event */ public JsonParser.Event getLastValueEvent() { return lastValueEvent; } /** * Set last obtained {@link JsonParser.Event} event. * * @param lastValueEvent last obtained event */ public void setLastValueEvent(JsonParser.Event lastValueEvent) { this.lastValueEvent = lastValueEvent; } /** * Return customization used by currently processed user defined deserializer. * * @return currently used customization */ public Customization getCustomization() { return customization; } /** * Set customization used by currently processed user defined deserializer. * * @param customization currently used customization */ public void setCustomization(Customization customization) { this.customization = customization; } @Override public T deserialize(Class clazz, JsonParser parser) { return deserializeItem(clazz, parser); } @Override public T deserialize(Type type, JsonParser parser) { return deserializeItem(type, parser); } @SuppressWarnings("unchecked") private T deserializeItem(Type type, JsonParser parser) { try { if (lastValueEvent == null) { lastValueEvent = parser.next(); checkState(); } ModelDeserializer modelDeserializer = getJsonbContext().getChainModelCreator().deserializerChain(type); return (T) modelDeserializer.deserialize(parser, this); } catch (JsonbException e) { throw e; } catch (RuntimeException e) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e); } } private void checkState() { if (lastValueEvent == JsonParser.Event.KEY_NAME) { throw new JsonbException("JsonParser has incorrect position as the first event: KEY_NAME"); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/InstanceCreator.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.function.Supplier; /** * Creates instances for known types, caches constructors of unknown. * (Constructors of parsed types are stored in {@link org.eclipse.yasson.internal.model.ClassModel}). */ public class InstanceCreator { private static final Map, Supplier> CREATORS = new HashMap<>(); static { CREATORS.put(ArrayList.class, ArrayList::new); CREATORS.put(LinkedList.class, LinkedList::new); CREATORS.put(HashSet.class, HashSet::new); CREATORS.put(TreeSet.class, TreeSet::new); CREATORS.put(HashMap.class, HashMap::new); CREATORS.put(TreeMap.class, TreeMap::new); } private InstanceCreator() { throw new IllegalStateException("This class should never be instantiated"); } /** * Create an instance of the given class with its default constructor. * * @param tClass class to create instance * @param Type of the class/instance * @return crated instance */ @SuppressWarnings("unchecked") public static T createInstance(Class tClass) { Supplier creator = (Supplier) CREATORS.get(tClass); //No worries for race conditions here, instance may be replaced during first attempt. if (creator == null) { Constructor constructor = ReflectionUtils.getDefaultConstructor(tClass, true); creator = () -> ReflectionUtils.createNoArgConstructorInstance(constructor); CREATORS.put(tClass, creator); } return creator.get(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonBinding.java ================================================ /* * Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.io.FilterWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.Map; import java.util.Set; import jakarta.json.JsonStructure; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.spi.JsonProvider; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.YassonJsonb; import org.eclipse.yasson.internal.jsonstructure.JsonGeneratorToStructureAdapter; import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter; /** * Implementation of Jsonb interface. */ public class JsonBinding implements YassonJsonb { private final JsonbContext jsonbContext; JsonBinding(JsonBindingBuilder builder) { this.jsonbContext = new JsonbContext(builder.getConfig(), builder.getProvider().orElseGet(JsonProvider::provider)); Set> eagerInitClasses = this.jsonbContext.getConfigProperties().getEagerInitClasses(); for (Class eagerInitClass : eagerInitClasses) { // Eagerly initialize requested ClassModels and Serializers jsonbContext.getChainModelCreator().deserializerChain(eagerInitClass); jsonbContext.getSerializationModelCreator().serializerChain(eagerInitClass, true, true); } } private T deserialize(final Type type, final JsonParser parser, final DeserializationContextImpl unmarshaller) { return unmarshaller.deserialize(type, parser); } @Override public T fromJson(String str, Class type) throws JsonbException { try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) { final DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return deserialize(type, parser, unmarshaller); } } @Override public T fromJson(String str, Type type) throws JsonbException { try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return deserialize(type, parser, unmarshaller); } } @Override public T fromJson(Reader reader, Class type) throws JsonbException { try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return deserialize(type, parser, unmarshaller); } } @Override public T fromJson(Reader reader, Type type) throws JsonbException { try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return deserialize(type, parser, unmarshaller); } } @Override public T fromJson(InputStream stream, Class clazz) throws JsonbException { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); try (JsonParser parser = inputStreamParser(stream)) { return deserialize(clazz, parser, unmarshaller); } } @Override public T fromJson(InputStream stream, Type type) throws JsonbException { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); try (JsonParser parser = inputStreamParser(stream)) { return deserialize(type, parser, unmarshaller); } } @Override public T fromJsonStructure(JsonStructure jsonStructure, Class type) throws JsonbException { try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) { return deserialize(type, parser, new DeserializationContextImpl(jsonbContext)); } } @Override public T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException { try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) { return deserialize(runtimeType, parser, new DeserializationContextImpl(jsonbContext)); } } private JsonParser inputStreamParser(InputStream stream) { return jsonbContext.getJsonParserFactory() .createParser(stream, Charset.forName((String) jsonbContext.getConfig() .getProperty(JsonbConfig.ENCODING).orElse("UTF-8"))); } @Override public String toJson(Object object) throws JsonbException { StringWriter writer = new StringWriter(); try (JsonGenerator generator = writerGenerator(writer)) { new SerializationContextImpl(jsonbContext).marshall(object, generator); } return writer.toString(); } @Override public String toJson(Object object, Type type) throws JsonbException { StringWriter writer = new StringWriter(); try (JsonGenerator generator = writerGenerator(writer)) { new SerializationContextImpl(jsonbContext, type).marshall(object, generator); } return writer.toString(); } @Override public void toJson(Object object, Writer writer) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext); try (JsonGenerator generator = writerGenerator(new CloseSuppressingWriter(writer))) { marshaller.marshallWithoutClose(object, generator); } } @Override public void toJson(Object object, Type type, Writer writer) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, type); try (JsonGenerator generator = writerGenerator(new CloseSuppressingWriter(writer))) { marshaller.marshallWithoutClose(object, generator); } } private JsonGenerator writerGenerator(Writer writer) { Map factoryProperties = jsonbContext.createJsonpProperties(jsonbContext.getConfig()); if (factoryProperties.isEmpty()) { return jsonbContext.getJsonProvider().createGenerator(writer); } return jsonbContext.getJsonProvider().createGeneratorFactory(factoryProperties).createGenerator(writer); } @Override public void toJson(Object object, OutputStream stream) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext); try (JsonGenerator generator = streamGenerator(stream)) { marshaller.marshall(object, generator); } } @Override public void toJson(Object object, Type type, OutputStream stream) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, type); try (JsonGenerator generator = streamGenerator(stream)) { marshaller.marshall(object, generator); } } @Override public T fromJson(JsonParser jsonParser, Class type) throws JsonbException { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return unmarshaller.deserialize(type, jsonParser); } @Override public T fromJson(JsonParser jsonParser, Type runtimeType) throws JsonbException { DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext); return unmarshaller.deserialize(runtimeType, jsonParser); } @Override public void toJson(Object object, JsonGenerator jsonGenerator) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext); marshaller.marshallWithoutClose(object, jsonGenerator); } @Override public void toJson(Object object, Type runtimeType, JsonGenerator jsonGenerator) throws JsonbException { final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, runtimeType); marshaller.marshallWithoutClose(object, jsonGenerator); } @Override public JsonStructure toJsonStructure(Object object) throws JsonbException { JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider()); final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext); marshaller.marshall(object, structureGenerator); return structureGenerator.getRootStructure(); } @Override public JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException { JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider()); final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, runtimeType); marshaller.marshall(object, structureGenerator); return structureGenerator.getRootStructure(); } private JsonGenerator streamGenerator(OutputStream stream) { Map factoryProperties = jsonbContext.createJsonpProperties(jsonbContext.getConfig()); final String encoding = (String) jsonbContext.getConfig().getProperty(JsonbConfig.ENCODING).orElse("UTF-8"); return jsonbContext.getJsonProvider().createGeneratorFactory(factoryProperties) .createGenerator(stream, Charset.forName(encoding)); } @Override public void close() throws Exception { jsonbContext.getComponentInstanceCreator().close(); } private static class CloseSuppressingWriter extends FilterWriter { protected CloseSuppressingWriter(final Writer in) { super(in); } @Override public void close() { // do not close } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.util.Optional; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; /** * JsonbBuilder implementation. */ public class JsonBindingBuilder implements JsonbBuilder { private JsonbConfig config = new JsonbConfig(); private JsonProvider provider = null; @Override public JsonbBuilder withConfig(JsonbConfig config) { this.config = config; return this; } @Override public JsonbBuilder withProvider(JsonProvider jsonpProvider) { this.provider = jsonpProvider; return this; } /** * Gets configuration. * * @return configuration. */ public JsonbConfig getConfig() { return config; } /** * Gets provider. * * @return Provider. */ public Optional getProvider() { return Optional.ofNullable(provider); } @Override public Jsonb build() { return new JsonBinding(this); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonbConfigProperties.java ================================================ /* * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.security.AccessController; import java.security.PrivilegedAction; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.function.Consumer; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.config.BinaryDataStrategy; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.bind.config.PropertyOrderStrategy; import jakarta.json.bind.config.PropertyVisibilityStrategy; import jakarta.json.bind.serializer.JsonbSerializer; import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.internal.model.PropertyModel; import org.eclipse.yasson.internal.model.ReverseTreeMap; import org.eclipse.yasson.internal.model.customization.PropertyOrdering; import org.eclipse.yasson.internal.model.customization.StrategiesProvider; import org.eclipse.yasson.internal.model.customization.VisibilityStrategiesProvider; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Resolved properties from JSONB config. */ @SuppressWarnings("rawtypes") public class JsonbConfigProperties { private static final Map> PROPERTY_ORDER_STRATEGY_MAPS = Map.of(PropertyOrderStrategy.LEXICOGRAPHICAL, TreeMap.class, PropertyOrderStrategy.REVERSE, ReverseTreeMap.class, PropertyOrderStrategy.ANY, HashMap.class); private final JsonbConfig jsonbConfig; private final PropertyVisibilityStrategy propertyVisibilityStrategy; private final PropertyNamingStrategy propertyNamingStrategy; private final PropertyOrdering propertyOrdering; private final JsonbDateFormatter dateFormatter; private final Locale locale; private final String binaryDataStrategy; private final boolean nullable; private final boolean failOnUnknownProperties; private final boolean strictIJson; private final boolean zeroTimeDefaulting; private final boolean requiredCreatorParameters; private final boolean dateInMillisecondsAsString; private final Map, Class> userTypeMapping; private final Class defaultMapImplType; private final JsonbSerializer nullSerializer; private final Set> eagerInitClasses; private final boolean forceMapArraySerializerForNullKeys; /** * Creates new resolved JSONB config. * * @param jsonbConfig jsonb config */ public JsonbConfigProperties(JsonbConfig jsonbConfig) { this.jsonbConfig = jsonbConfig; this.binaryDataStrategy = initBinaryDataStrategy(); this.propertyNamingStrategy = initPropertyNamingStrategy(); this.propertyVisibilityStrategy = initPropertyVisibilityStrategy(); this.propertyOrdering = new PropertyOrdering(initOrderStrategy()); this.locale = initConfigLocale(); this.dateFormatter = initDateFormatter(this.locale); this.nullable = initConfigNullable(); this.failOnUnknownProperties = initConfigFailOnUnknownProperties(); this.strictIJson = initStrictJson(); this.userTypeMapping = initUserTypeMapping(); this.zeroTimeDefaulting = initZeroTimeDefaultingForJavaTime(); this.defaultMapImplType = initDefaultMapImplType(); this.nullSerializer = initNullSerializer(); this.eagerInitClasses = initEagerInitClasses(); this.requiredCreatorParameters = initRequiredCreatorParameters(); this.forceMapArraySerializerForNullKeys = initForceMapArraySerializerForNullKeys(); this.dateInMillisecondsAsString = initDateInMillisecondsAsString(); } private Class initDefaultMapImplType() { //We need to get PropertyOrderStrategy again. This time, if was not set, use ANY to get proper map implementation. //This is intentional! String propertyOrder = getConfigProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, String.class, PropertyOrderStrategy.ANY); return PROPERTY_ORDER_STRATEGY_MAPS.getOrDefault(propertyOrder, HashMap.class); } private boolean initZeroTimeDefaultingForJavaTime() { return getConfigProperty(YassonConfig.ZERO_TIME_PARSE_DEFAULTING, Boolean.class, false); } @SuppressWarnings("unchecked") private Map, Class> initUserTypeMapping() { return getConfigProperty(YassonConfig.USER_TYPE_MAPPING, Map.class, Collections.emptyMap()); } private JsonbDateFormatter initDateFormatter(Locale locale) { final String dateFormat = getGlobalConfigJsonbDateFormat(); if (JsonbDateFormat.DEFAULT_FORMAT.equals(dateFormat) || JsonbDateFormat.TIME_IN_MILLIS.equals(dateFormat)) { return new JsonbDateFormatter(dateFormat, locale.toLanguageTag()); } DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().appendPattern(dateFormat); if (isZeroTimeDefaulting()) { builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0); builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0); builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0); } return new JsonbDateFormatter(builder.toFormatter(locale), dateFormat, locale.toLanguageTag()); } private String getGlobalConfigJsonbDateFormat() { return getConfigProperty(JsonbConfig.DATE_FORMAT, String.class, JsonbDateFormat.DEFAULT_FORMAT); } private Consumer> initOrderStrategy() { return StrategiesProvider.getOrderingFunction(getPropertyOrderStrategy()); } private String getPropertyOrderStrategy() { return getConfigProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, String.class, PropertyOrderStrategy.LEXICOGRAPHICAL); } private PropertyNamingStrategy initPropertyNamingStrategy() { final Optional property = jsonbConfig.getProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY); if (property.isEmpty()) { return StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY); } Object propertyNamingStrategy = property.get(); if (propertyNamingStrategy instanceof String) { return StrategiesProvider.getPropertyNamingStrategy((String) propertyNamingStrategy); } else if (!(propertyNamingStrategy instanceof PropertyNamingStrategy)) { throw new JsonbException(Messages.getMessage(MessageKeys.PROPERTY_NAMING_STRATEGY_INVALID)); } return (PropertyNamingStrategy) property.get(); } private PropertyVisibilityStrategy initPropertyVisibilityStrategy() { final Optional property = jsonbConfig.getProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY); if (property.isEmpty()) { return null; } final Object propertyVisibilityStrategy = property.get(); if (propertyVisibilityStrategy instanceof String) { return VisibilityStrategiesProvider.getStrategy((String) propertyVisibilityStrategy); } else if (!(propertyVisibilityStrategy instanceof PropertyVisibilityStrategy)) { throw new JsonbException("JsonbConfig.PROPERTY_VISIBILITY_STRATEGY must be instance of " + PropertyVisibilityStrategy.class); } return (PropertyVisibilityStrategy) propertyVisibilityStrategy; } private String initBinaryDataStrategy() { if (getConfigProperty(JsonbConfig.STRICT_IJSON, Boolean.class, false)) { return BinaryDataStrategy.BASE_64_URL; } return getConfigProperty(JsonbConfig.BINARY_DATA_STRATEGY, String.class, BinaryDataStrategy.BYTE); } private boolean initConfigNullable() { return getConfigProperty(JsonbConfig.NULL_VALUES, Boolean.class, false); } private boolean initConfigFailOnUnknownProperties() { return getConfigProperty(YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES, Boolean.class, false); } private boolean initRequiredCreatorParameters() { String sysProp = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(JsonbConfig.CREATOR_PARAMETERS_REQUIRED)); if (sysProp != null) { return Boolean.parseBoolean(sysProp); } return getConfigProperty(JsonbConfig.CREATOR_PARAMETERS_REQUIRED, Boolean.class, false); } private boolean initDateInMillisecondsAsString() { String sysProp = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING)); if (sysProp != null) { return Boolean.parseBoolean(sysProp); } return getConfigProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING, Boolean.class, false); } @SuppressWarnings("unchecked") private JsonbSerializer initNullSerializer() { return jsonbConfig.getProperty(YassonConfig.NULL_ROOT_SERIALIZER) .map(o -> { if (!(o instanceof JsonbSerializer)) { throw new JsonbException("YassonConfig.NULL_ROOT_SERIALIZER must be instance of " + JsonbSerializer.class + ""); } return (JsonbSerializer) o; }).orElse(null); } private Set> initEagerInitClasses() { Optional property = jsonbConfig.getProperty(YassonConfig.EAGER_PARSE_CLASSES); if (property.isEmpty()) { return Collections.emptySet(); } Object eagerInitClasses = property.get(); if (!(eagerInitClasses instanceof Class[])) { throw new JsonbException("YassonConfig.EAGER_PARSE_CLASSES must be instance of Class[]"); } return new HashSet<>(Arrays.asList((Class[]) eagerInitClasses)); } private boolean initForceMapArraySerializerForNullKeys() { return getConfigProperty(YassonConfig.FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS, Boolean.class, false); } /** * Gets nullable from {@link JsonbConfig}. * If true null values are serialized to json. * * @return Configured nullable */ public boolean getConfigNullable() { return nullable; } /** * Gets unknown properties flag from {@link JsonbConfig}. * If false, {@link JsonbException} is not thrown for deserialization, when json key * cannot be mapped to class property. * * @return {@link JsonbException} is risen on unknown property. Default is true even if * not set in json config. */ public boolean getConfigFailOnUnknownProperties() { return failOnUnknownProperties; } private T getConfigProperty(String propertyName, Class propertyType, T defaultValue) { Objects.requireNonNull(defaultValue, "Default value cannot be null"); return jsonbConfig.getProperty(propertyName) .or(() -> Optional.of(defaultValue)) .filter(propertyType::isInstance) .map(propertyType::cast) .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_PROPERTY_INVALID_TYPE, propertyName, propertyType.getSimpleName()))); } /** * Checks for binary data strategy to use. * * @return Binary data strategy. */ public String getBinaryDataStrategy() { return binaryDataStrategy; } /** * Converts string locale to {@link Locale}. * * @param locale Locale to convert. * @return {@link Locale} instance. */ public Locale getLocale(String locale) { if (locale.equals(JsonbDateFormat.DEFAULT_LOCALE)) { return this.locale; } return Locale.forLanguageTag(locale); } /** * Gets locale from {@link JsonbConfig}. * * @return Configured locale. */ private Locale initConfigLocale() { return getConfigProperty(JsonbConfig.LOCALE, Locale.class, Locale.getDefault()); } private boolean initStrictJson() { return getConfigProperty(JsonbConfig.STRICT_IJSON, Boolean.class, false); } /** * Gets property visibility strategy. * * @return Property visibility strategy. */ public PropertyVisibilityStrategy getPropertyVisibilityStrategy() { return propertyVisibilityStrategy; } /** * Gets property naming strategy. * * @return Property naming strategy. */ public PropertyNamingStrategy getPropertyNamingStrategy() { return propertyNamingStrategy; } /** * Gets instantiated shared config date formatter. * * @return Date formatter. */ public JsonbDateFormatter getConfigDateFormatter() { return dateFormatter; } /** * Gets property ordering component. * * @return Component for ordering properties. */ public PropertyOrdering getPropertyOrdering() { return propertyOrdering; } /** * If strict IJSON patterns should be used. * * @return if IJSON is enabled */ public boolean isStrictIJson() { return strictIJson; } /** * User type mapping for map interface to implementation classes. * * @return User type mapping. */ public Map, Class> getUserTypeMapping() { return userTypeMapping; } /** *

Makes parsing dates defaulting to zero hour, minute and second. * This will made available to parse patterns like yyyy.MM.dd to * {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate} * or even {@link java.time.ZonedDateTime}. *

If time zone is not set in the pattern than UTC time zone is used. * So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed * to instant {@link java.time.Instant}. * * @return true if time should be defaulted to zero. */ public boolean isZeroTimeDefaulting() { return zeroTimeDefaulting; } /** * Default {@link java.util.Map} implementation to use, based on order strategy. * * @return map impl type */ public Class getDefaultMapImplType() { return defaultMapImplType; } public JsonbSerializer getNullSerializer() { return nullSerializer; } public boolean hasRequiredCreatorParameters() { return requiredCreatorParameters; } public Set> getEagerInitClasses() { return eagerInitClasses; } /** * Whether the MapToEntriesArraySerializer is selected when a null key * is detected in a map. * * @return false or true */ public boolean isForceMapArraySerializerForNullKeys() { return forceMapArraySerializerForNullKeys; } public boolean isDateInMillisecondsAsString() { return dateInMillisecondsAsString; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonbContext.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import java.util.logging.Logger; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.spi.JsonProvider; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParserFactory; import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory; import org.eclipse.yasson.internal.deserializer.DeserializationModelCreator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.eclipse.yasson.internal.serializer.SerializationModelCreator; import org.eclipse.yasson.spi.JsonbComponentInstanceCreator; /** * Jsonb context holding central components and configuration of jsonb runtime. Scoped to instance of Jsonb runtime. */ public class JsonbContext { private static final Logger LOGGER = Logger.getLogger(JsonbContext.class.getName()); private final JsonbConfig jsonbConfig; private final MappingContext mappingContext; private final DeserializationModelCreator deserializationModelCreator; private final SerializationModelCreator serializationModelCreator; private final JsonbComponentInstanceCreator componentInstanceCreator; private final JsonProvider jsonProvider; private final JsonParserFactory jsonParserFactory; private final ComponentMatcher componentMatcher; private final AnnotationIntrospector annotationIntrospector; private final JsonbConfigProperties configProperties; /** * Creates and initialize context. * * @param jsonbConfig jsonb jsonbConfig not null * @param jsonProvider provider of JSONP */ public JsonbContext(JsonbConfig jsonbConfig, JsonProvider jsonProvider) { Objects.requireNonNull(jsonbConfig); this.jsonbConfig = jsonbConfig; this.mappingContext = new MappingContext(this); this.componentInstanceCreator = initComponentInstanceCreator(); this.componentMatcher = new ComponentMatcher(this); this.annotationIntrospector = new AnnotationIntrospector(this); this.jsonProvider = jsonProvider; this.jsonParserFactory = initJsonParserFactory(); this.configProperties = new JsonbConfigProperties(jsonbConfig); this.deserializationModelCreator = new DeserializationModelCreator(this); this.serializationModelCreator = new SerializationModelCreator(this); } /** * Gets {@link JsonbConfig}. * * @return Configuration. */ public JsonbConfig getConfig() { return jsonbConfig; } /** * Gets mapping context. * * @return Mapping context. */ public MappingContext getMappingContext() { return mappingContext; } /** * Get chain model creator. * * @return chain model creator */ public DeserializationModelCreator getChainModelCreator() { return deserializationModelCreator; } /** * Get serialization model creator. * * @return serialization model creator */ public SerializationModelCreator getSerializationModelCreator() { return serializationModelCreator; } /** * Gets JSONP provider. * * @return JSONP provider. */ public JsonProvider getJsonProvider() { return jsonProvider; } /** * Implementation creating instances of user components used by JSONB, such as adapters and strategies. * * @return Instance creator. */ public JsonbComponentInstanceCreator getComponentInstanceCreator() { return componentInstanceCreator; } /** * Component matcher for lookup of (de)serializers and adapters. * * @return Component matcher. */ public ComponentMatcher getComponentMatcher() { return componentMatcher; } /** * Gets component for annotation parsing. * * @return Annotation introspector. */ public AnnotationIntrospector getAnnotationIntrospector() { return annotationIntrospector; } public JsonbConfigProperties getConfigProperties() { return configProperties; } public JsonParserFactory getJsonParserFactory() { return jsonParserFactory; } private JsonParserFactory initJsonParserFactory() { return jsonProvider.createParserFactory(createJsonpProperties(jsonbConfig)); } /** * Propagates properties from JsonbConfig to JSONP generator / parser factories. * * @param jsonbConfig jsonb config * @return properties for JSONP generator / parser */ protected Map createJsonpProperties(JsonbConfig jsonbConfig) { //JSONP 1.0 actually ignores the value, just checks the key is present. Only set if JsonbConfig.FORMATTING is true. final Optional property = jsonbConfig.getProperty(JsonbConfig.FORMATTING); final Map factoryProperties = new HashMap<>(); if (property.isPresent()) { final Object value = property.get(); if (!(value instanceof Boolean)) { throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_FORMATTING_ILLEGAL_VALUE)); } if ((Boolean) value) { factoryProperties.put(JsonGenerator.PRETTY_PRINTING, Boolean.TRUE); } return factoryProperties; } return factoryProperties; } private JsonbComponentInstanceCreator initComponentInstanceCreator() { ServiceLoader loader = AccessController .doPrivileged((PrivilegedAction>) () -> ServiceLoader .load(JsonbComponentInstanceCreator.class)); List creators = new ArrayList<>(); for (JsonbComponentInstanceCreator creator : loader) { creators.add(creator); } if (creators.isEmpty()) { // No service provider found - use the defaults return JsonbComponentInstanceCreatorFactory.getComponentInstanceCreator(); } creators.sort(Comparator.comparingInt(JsonbComponentInstanceCreator::getPriority).reversed()); JsonbComponentInstanceCreator creator = creators.get(0); LOGGER.finest("Component instance creator:" + creator.getClass()); return creator; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonbDateFormatter.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.Locale; import java.util.Objects; import jakarta.json.bind.annotation.JsonbDateFormat; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; /** * Formatter wrapper for different types of dates. */ public class JsonbDateFormatter { private static final JsonbDateFormatter DEFAULT = new JsonbDateFormatter(JsonbDateFormat.DEFAULT_FORMAT, Locale.getDefault().toLanguageTag()); /** * Default I-JSON date time formatter. */ public static final DateTimeFormatter IJSON_DATE_FORMATTER = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(DateTimeFormatter.ISO_LOCAL_DATE) .appendLiteral('T') .appendValue(HOUR_OF_DAY, 2) .appendLiteral(':') .appendValue(MINUTE_OF_HOUR, 2) .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2) .appendLiteral('Z') .appendOffset("+HH:MM", "+00:00") .toFormatter(); private final DateTimeFormatter dateTimeFormatter; private final String format; private final String locale; /** * Creates an instance with cached {@link DateTimeFormatter}, format and locale. * * @param dateTimeFormatter Reused time formatter. * @param format Format in string. * @param locale Locale in string. */ public JsonbDateFormatter(DateTimeFormatter dateTimeFormatter, String format, String locale) { this.dateTimeFormatter = dateTimeFormatter; this.format = format; this.locale = locale; } /** * Creates an instance with format string and locale. * Formatter will be created on every formatting / parsing operation. * * @param format Formatter format. * @param locale Locale in string. */ public JsonbDateFormatter(String format, String locale) { this.format = format; this.locale = locale; this.dateTimeFormatter = null; } /** * Creates an instance with cached instance of {@link DateTimeFormatter}. * * @return Formatter instance. */ public DateTimeFormatter getDateTimeFormatter() { return dateTimeFormatter; } /** * Format string to be used either by formatter. * Needed for formatting {@link java.util.Date} with {@link java.text.SimpleDateFormat}, * which is not threadsafe. * * @return Format. */ public String getFormat() { return format; } /** * Locale to use with formatter. * * @return Locale. */ public String getLocale() { return locale; } public static JsonbDateFormatter getDefault() { return DEFAULT; } public boolean isDefault() { return JsonbDateFormat.DEFAULT_FORMAT.equals(format); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JsonbDateFormatter that = (JsonbDateFormatter) o; return Objects.equals(format, that.format) && Objects.equals(locale, that.locale) && Objects.equals(dateTimeFormatter, that.dateTimeFormatter); } @Override public int hashCode() { return Objects.hash(dateTimeFormatter, format, locale); } @Override public String toString() { return "JsonbDateFormatter{" + "dateTimeFormatter=" + dateTimeFormatter + ", format='" + format + '\'' + ", locale='" + locale + '\'' + '}'; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/JsonbNumberFormatter.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.util.Objects; /** * Formatter for numbers. */ public class JsonbNumberFormatter { private final String format; private final String locale; /** * Construct with format string and locale. * * @param format formatter format * @param locale locale */ public JsonbNumberFormatter(String format, String locale) { this.format = format; this.locale = locale; } /** * Format string to be used either by formatter. * * @return format */ public String getFormat() { return format; } /** * Locale to use with formatter. * * @return locale */ public String getLocale() { return locale; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JsonbNumberFormatter that = (JsonbNumberFormatter) o; return Objects.equals(format, that.format) && Objects.equals(locale, that.locale); } @Override public int hashCode() { return Objects.hash(format, locale); } @Override public String toString() { return "JsonbNumberFormatter{" + "format='" + format + '\'' + ", locale='" + locale + '\'' + '}'; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/MappingContext.java ================================================ /* * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.util.ArrayDeque; import java.util.Deque; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.JsonbAnnotatedElement; import org.eclipse.yasson.internal.model.customization.ClassCustomization; /** * JSONB mappingContext. Created once per {@link jakarta.json.bind.Jsonb} instance. Represents a global scope. * Holds internal model. * * Thread safe. */ public class MappingContext { private final JsonbContext jsonbContext; private final ConcurrentHashMap, ClassModel> classes = new ConcurrentHashMap<>(); private final ClassParser classParser; /** * Create mapping context which is scoped to jsonb runtime. * * @param jsonbContext Context. Required. */ public MappingContext(JsonbContext jsonbContext) { Objects.requireNonNull(jsonbContext); this.jsonbContext = jsonbContext; this.classParser = new ClassParser(jsonbContext); } /** * Searches for class model for given class. Returns the existing instance. Creates a new instance if * it doesn't exist. * * @param clazz Class to search by or parse, not null. * @return {@link ClassModel} for given class. */ public ClassModel getOrCreateClassModel(Class clazz) { ClassModel classModel = classes.get(clazz); if (classModel != null) { return classModel; } Deque> newClassModels = new ArrayDeque<>(); for (Class classToParse = clazz; classToParse != Object.class; classToParse = classToParse.getSuperclass()) { if (classToParse == null) { break; } newClassModels.push(classToParse); } if (clazz == Object.class) { return classes.computeIfAbsent(clazz, (c) -> new ClassModel(c, ClassCustomization.empty(), null, null)); } ClassModel parentClassModel = null; while (!newClassModels.isEmpty()) { Class toParse = newClassModels.pop(); parentClassModel = classes .computeIfAbsent(toParse, createParseClassModelFunction(parentClassModel, classParser, jsonbContext)); } return classes.get(clazz); } private static Function, ClassModel> createParseClassModelFunction(ClassModel parentClassModel, ClassParser classParser, JsonbContext jsonbContext) { return aClass -> { JsonbAnnotatedElement> clsElement = jsonbContext.getAnnotationIntrospector().collectAnnotations(aClass); ClassCustomization customization = jsonbContext.getAnnotationIntrospector() .introspectCustomization(clsElement, parentClassModel == null ? ClassCustomization.empty() : parentClassModel.getClassCustomization(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()); // PolymorphismSupport configPolymorphism = jsonbContext.getConfigProperties().getPolymorphismSupport(); // if (configPolymorphism != null) { // customization = mergeConfigAndAnnotationPolymorphism(configPolymorphism, // configPolymorphism.getClassPolymorphism(aClass), // customization, // aClass); // } ClassModel newClassModel = new ClassModel(aClass, customization, parentClassModel, jsonbContext.getConfigProperties().getPropertyNamingStrategy()); if (!BuiltInTypes.isKnownType(aClass)) { classParser.parseProperties(newClassModel, clsElement); } return newClassModel; }; } // private static ClassCustomization mergeConfigAndAnnotationPolymorphism(PolymorphismSupport generalPolymorphism, // Optional maybeClassPolymorphism, // ClassCustomization customization, // Class aClass) { // PolymorphismConfig polymorphismConfig = customization.getPolymorphismConfig(); // PolymorphismConfig.Builder polyConfigBuilder; // if (polymorphismConfig != null) { // polyConfigBuilder = PolymorphismConfig.builder().of(polymorphismConfig); // } else { // polyConfigBuilder = PolymorphismConfig.builder(); // maybeClassPolymorphism.ifPresent(classPolymorphism -> polyConfigBuilder // .inherited(!classPolymorphism.getBoundClass().equals(aClass))); // } // generalPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); // generalPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); // polyConfigBuilder.whitelistedPackages(generalPolymorphism.getWhitelistedPackages()); // // maybeClassPolymorphism.ifPresent(classPolymorphism -> { // classPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName); // classPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames); // classPolymorphism.getFormat().ifPresent(polyConfigBuilder::format); // classPolymorphism.getAliases().forEach(polyConfigBuilder::alias); // polyConfigBuilder.whitelistedPackages(classPolymorphism.getWhitelistedPackages()); // }); // PolymorphismConfig polyConfigMerged = polyConfigBuilder.build(); // if (polyConfigMerged.getFieldName() == null || polyConfigMerged.getFieldName().isEmpty()) { // throw new JsonbException("Polymorphism type field name cannot be null or empty: " + aClass); // } // return ClassCustomization.builder() // .of(customization) // .polymorphismConfig(polyConfigMerged) // .build(); // } /** * Search for class model, without parsing if not found. * * @param clazz Class to search by or parse, not null. * @return Model of a class if found. */ public ClassModel getClassModel(Class clazz) { return classes.get(clazz); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ProcessingContext.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.util.HashSet; import java.util.Set; /** * Jsonb processing (serializing/deserializing) context. * Instance is thread bound (in contrast to {@link JsonbContext}. */ public abstract class ProcessingContext { private final JsonbContext jsonbContext; /** * Used to avoid StackOverflowError, when adapted / serialized object * contains instance of its type inside it or when object has recursive reference. */ private final Set currentlyProcessedObjects = new HashSet<>(); /** * Parent for marshaller and unmarshaller. * * @param jsonbContext context of Jsonb */ public ProcessingContext(JsonbContext jsonbContext) { this.jsonbContext = jsonbContext; } /** * Jsonb context. * * @return jsonb context */ public JsonbContext getJsonbContext() { return jsonbContext; } /** * Mapping context. * * @return mapping context */ public MappingContext getMappingContext() { return getJsonbContext().getMappingContext(); } /** * Adds currently processed object to the {@link Set}. * * @param object processed object * @return if object was added */ public boolean addProcessedObject(Object object) { return this.currentlyProcessedObjects.add(object); } /** * Removes processed object from the {@link Set}. * * @param object processed object * @return if object was removed */ public boolean removeProcessedObject(Object object) { return currentlyProcessedObjects.remove(object); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java ================================================ /* * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Constructor; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.logging.Logger; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Utility class for resolution of generics during unmarshalling. */ public class ReflectionUtils { private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName()); private ReflectionUtils() { throw new IllegalStateException("Utility classes should not be instantiated."); } /** * Get raw type by type. * Only for ParametrizedTypes, GenericArrayTypes and Classes. * * Empty optional is returned if raw type cannot be resolved. * * @param type Type to get class information from, not null. * @return Class of a type. */ public static Optional> getOptionalRawType(Type type) { if (type instanceof Class) { return Optional.of((Class) type); } else if (type instanceof ParameterizedType) { return Optional.of((Class) ((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { return Optional.of(((GenericArrayType) type).getClass()); } else if (type instanceof TypeVariable) { TypeVariable typeVariable = TypeVariable.class.cast(type); if (Objects.nonNull(typeVariable.getBounds())) { Optional> specializedClass = Optional.empty(); for (Type bound : typeVariable.getBounds()) { Optional> boundRawType = getOptionalRawType(bound); if (boundRawType.isPresent() && !Object.class.equals(boundRawType.get())) { if (!specializedClass.isPresent() || specializedClass.get().isAssignableFrom(boundRawType.get())) { specializedClass = Optional.of(boundRawType.get()); } } } return specializedClass; } } return Optional.empty(); } /** * Get raw type by type. * Resolves only ParametrizedTypes, GenericArrayTypes and Classes. * * Exception is thrown if raw type cannot be resolved. * * @param type Type to get class information from, not null. * @return Class of a raw type. */ public static Class getRawType(Type type) { return getOptionalRawType(type) .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.TYPE_RESOLUTION_ERROR, type))); } /** * Get a raw type of any type. * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar. * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific * bound is a {@link TypeVariable}, perform typevar resolution. * * @param chain hierarchy of all wrapping types. * @param type type to resolve, typically field type or generic bound, not null. * @return resolved raw class */ public static Class resolveRawType(List chain, Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return (Class) ((ParameterizedType) type).getRawType(); } else { return getRawType(resolveType(chain, type)); } } /** * Resolve a type by chain. * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar. * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific * bound is a {@link TypeVariable}, perform typevar resolution. * * @param chain hierarchy of all wrapping types. * @param type type to resolve, typically field type or generic bound, not null. * @return resolved type */ public static Type resolveType(List chain, Type type) { return resolveType(chain, type, true); } private static Type resolveType(List chain, Type type, boolean warn) { Type toResolve = type; if (type instanceof GenericArrayType) { toResolve = ((GenericArrayType) type).getGenericComponentType(); Type resolved = resolveType(chain, toResolve); return new GenericArrayTypeImpl(resolved); } if (toResolve instanceof WildcardType) { return resolveMostSpecificBound(chain, (WildcardType) toResolve, warn); } else if (toResolve instanceof TypeVariable) { return resolveItemVariableType(chain, (TypeVariable) toResolve, warn); } else if (toResolve instanceof ParameterizedType) { return resolveTypeArguments((ParameterizedType) toResolve, chain.get(chain.size() - 1)); } return type; } /** * Resolves type by item information and wraps it with {@link Optional}. * * @param chain hierarchy of all wrapping types. * @param type type * @return resolved type wrapped with Optional */ public static Optional resolveOptionalType(List chain, Type type) { try { return Optional.of(resolveType(chain, type, false)); } catch (RuntimeException e) { return Optional.empty(); } } /** * Resolve a bounded type variable type by its wrapper types. * Resolution could be done only if a compile time generic information is provided, either: * by generic field or subclass of a generic class. * * @param chain chain to search "runtime" generic type of a TypeVariable. * @param typeVariable type to search in chain for, not null. * @param warn whether or not to log a warning message when bounds are not found * @return Type of a generic "runtime" bound, not null. */ public static Type resolveItemVariableType(List chain, TypeVariable typeVariable, boolean warn) { // if (chain == null) { // Optional> optionalRawType = getOptionalRawType(typeVariable); // if (optionalRawType.isPresent()) { // return optionalRawType.get(); // } // //Bound not found, treat it as an Object.class // if (warn) { // LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, // typeVariable, // typeVariable.getGenericDeclaration())); // } // return Object.class; // } Type returnType = typeVariable; for (int i = chain.size() - 1; i >= 0; i--) { Type type = chain.get(i); Type tmp = new VariableTypeInheritanceSearch().searchParametrizedType(type, (TypeVariable) returnType); if (tmp != null) { returnType = tmp; } // If the type is a WildcardType we need to resolve the most specific type if (returnType instanceof WildcardType) { return resolveMostSpecificBound(chain, (WildcardType) returnType, warn); } if (!(returnType instanceof TypeVariable)) { break; } } if (returnType instanceof TypeVariable) { // throw new JsonbException("Could not resolve: " + unresolvedType); return Object.class; } return returnType; // //Embedded items doesn't hold information about variable types // if (chain instanceof EmbeddedItem) { // return resolveItemVariableType(chain.getWrapper(), typeVariable, warn); // } // // ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(chain.getRuntimeType()); // // VariableTypeInheritanceSearch search = new VariableTypeInheritanceSearch(); // Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable); // if (foundType != null) { // if (foundType instanceof TypeVariable) { // return resolveItemVariableType(chain.getWrapper(), (TypeVariable) foundType, warn); // } // return foundType; // } // // return resolveItemVariableType(chain.getWrapper(), typeVariable, warn); } /** * Resolves {@link TypeVariable} arguments of generic types. * * @param typeToResolve type to resolve * @param typeToSearch type to search * @return resolved type */ public static Type resolveTypeArguments(ParameterizedType typeToResolve, Type typeToSearch) { final Type[] unresolvedArgs = typeToResolve.getActualTypeArguments(); Type[] resolvedArgs = new Type[unresolvedArgs.length]; for (int i = 0; i < unresolvedArgs.length; i++) { Type unresolvedArg = unresolvedArgs[i]; if (!(unresolvedArg instanceof TypeVariable) && !(unresolvedArg instanceof GenericArrayType)) { resolvedArgs[i] = unresolvedArg; } else { Type variableType = unresolvedArg; if (variableType instanceof GenericArrayType) { variableType = ((GenericArrayType) variableType).getGenericComponentType(); } resolvedArgs[i] = new VariableTypeInheritanceSearch() .searchParametrizedType(typeToSearch, (TypeVariable) variableType); if (resolvedArgs[i] == null) { Type[] bounds = ((TypeVariable) variableType).getBounds(); if (Objects.nonNull(bounds) && bounds.length > 0) { resolvedArgs[i] = bounds[0]; } } if (resolvedArgs[i] == null || resolvedArgs[i].equals(typeToResolve)) { if (typeToSearch instanceof Class) { return Object.class; } //No generic information available throw new IllegalStateException(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, variableType, typeToSearch)); } } if (resolvedArgs[i] instanceof ParameterizedType) { resolvedArgs[i] = resolveTypeArguments((ParameterizedType) resolvedArgs[i], typeToSearch); } else if (unresolvedArg instanceof GenericArrayType) { resolvedArgs[i] = new GenericArrayTypeImpl(resolvedArgs[i]); } } return Arrays.equals(resolvedArgs, unresolvedArgs) ? typeToResolve : new ResolvedParameterizedType(typeToResolve, resolvedArgs); } /** * Create instance with constructor. * * @param constructor const not null * @param type of instance * @return instance */ public static T createNoArgConstructorInstance(Constructor constructor) { Objects.requireNonNull(constructor); try { return constructor.newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new JsonbException("Can't create instance", e); } } /** * Get default no argument constructor of the class. * * @param clazz Class to get constructor from * @param Class generic type * @param required if true, throws an exception if the default constructor is missing. * If false, returns null in that case * @return the constructor of the class, or null. Depending on required. */ public static Constructor getDefaultConstructor(Class clazz, boolean required) { Objects.requireNonNull(clazz); return AccessController.doPrivileged((PrivilegedAction>) () -> { try { final Constructor declaredConstructor = clazz.getDeclaredConstructor(); if (declaredConstructor.getModifiers() == Modifier.PROTECTED) { declaredConstructor.setAccessible(true); } return declaredConstructor; } catch (NoSuchMethodException | RuntimeException e) { if (required) { throw new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, clazz), e); } return null; } }); } /** * For generic adapters like: *

* {@code * interface ContainerAdapter extends JsonbAdapter, Crate>...; * class IntegerBoxToCrateAdapter implements ContainerAdapter...; * } *

* We need to find a JsonbAdapter class which will hold basic generic type arguments, * and resolve them if they are TypeVariables from there. * * @param classToSearch class to resolve parameterized interface * @param parameterizedInterface interface to search * @return type of JsonbAdapter */ public static ParameterizedType findParameterizedType(Class classToSearch, Class parameterizedInterface) { Class current = classToSearch; while (current != Object.class) { for (Type currentInterface : current.getGenericInterfaces()) { if (currentInterface instanceof ParameterizedType && parameterizedInterface.isAssignableFrom( ReflectionUtils.getRawType(((ParameterizedType) currentInterface).getRawType()))) { return (ParameterizedType) currentInterface; } } current = current.getSuperclass(); } throw new JsonbException(Messages.getMessage(MessageKeys.NON_PARAMETRIZED_TYPE, parameterizedInterface)); } /** * Check if type needs resolution. If type is a class or a parametrized type with all type arguments as classes * than it is considered resolved. If any of types is type variable or wildcard type is not resolved. * * @param type Type to check. * @return True if resolved */ public static boolean isResolvedType(Type type) { if (type instanceof ParameterizedType) { for (Type typeArg : ((ParameterizedType) type).getActualTypeArguments()) { if (!isResolvedType(typeArg)) { return false; } } return true; } return type instanceof Class; } private static ParameterizedType findParameterizedSuperclass(Type type) { if (type == null || type instanceof ParameterizedType) { return (ParameterizedType) type; } if (!(type instanceof Class)) { throw new JsonbException("Can't resolve ParameterizedType superclass for: " + type); } return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); } /** * Resolves a wildcard most specific upper or lower bound. * * @param chain Type. * @param wildcardType Wildcard type. * @return The most specific type. */ private static Type resolveMostSpecificBound(List chain, WildcardType wildcardType, boolean warn) { Class result = Object.class; for (Type upperBound : wildcardType.getUpperBounds()) { result = getMostSpecificBound(chain, result, upperBound, warn); } for (Type lowerBound : wildcardType.getLowerBounds()) { result = getMostSpecificBound(chain, result, lowerBound, warn); } return result; } private static Class getMostSpecificBound(List chain, Class result, Type bound, boolean warn) { if (bound == Object.class) { return result; } //if bound is type variable search recursively for wrapper generic expansion Type resolvedBoundType = bound instanceof TypeVariable ? resolveType(chain, bound, warn) : bound; Class boundRawType = getRawType(resolvedBoundType); //resolved class is a subclass of a result candidate if (result.isAssignableFrom(boundRawType)) { result = boundRawType; } return result; } public static final class GenericArrayTypeImpl implements GenericArrayType { private final Type genericComponentType; // private constructor enforces use of static factory private GenericArrayTypeImpl(Type ct) { genericComponentType = ct; } /** * Returns a {@code Type} object representing the component type * of this array. * * @return a {@code Type} object representing the component type * of this array * @since 1.5 */ public Type getGenericComponentType() { return genericComponentType; // return cached component type } public String toString() { return getGenericComponentType().getTypeName() + "[]"; } @Override public boolean equals(Object o) { if (o instanceof GenericArrayType) { GenericArrayType that = (GenericArrayType) o; return Objects.equals(genericComponentType, that.getGenericComponentType()); } else { return false; } } @Override public int hashCode() { return Objects.hashCode(genericComponentType); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/ResolvedParameterizedType.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Objects; /** * {@link ParameterizedType} implementation containing array of resolved TypeVariable type args. */ class ResolvedParameterizedType implements ParameterizedType { /** * Original parameterized type. */ private final ParameterizedType original; /** * Resolved args by runtime type. */ private final Type[] resolvedTypeArgs; /** * Creates a new instance. * * @param original Original type. * @param resolvedTypeArgs Resolved type arguments. */ ResolvedParameterizedType(ParameterizedType original, Type[] resolvedTypeArgs) { this.original = original; this.resolvedTypeArgs = resolvedTypeArgs; } /** * Type arguments with resolved TypeVariables. * * @return type args */ @Override public Type[] getActualTypeArguments() { return resolvedTypeArgs; } @Override public Type getRawType() { return original.getRawType(); } @Override public Type getOwnerType() { return original.getOwnerType(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(original.toString()); if (resolvedTypeArgs != null && resolvedTypeArgs.length > 0) { sb.append(" resolved arguments: ["); for (Type typeArg : resolvedTypeArgs) { sb.append(typeArg); } sb.append("]"); } return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || !(o instanceof ParameterizedType)) { return false; } final ParameterizedType that = (ParameterizedType) o; return this.getRawType().equals(that.getRawType()) && Objects.equals(this.getOwnerType(), that.getOwnerType()) && Arrays.equals(resolvedTypeArgs, that.getActualTypeArguments()); } @Override public int hashCode() { return Arrays.hashCode(resolvedTypeArgs) ^ (getOwnerType() == null ? 0 : getOwnerType().hashCode()) ^ (getRawType() == null ? 0 : getRawType().hashCode()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/SerializationContextImpl.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.logging.Logger; import jakarta.json.bind.JsonbException; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerationException; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * JSONB marshaller. Created each time marshalling operation called. */ public class SerializationContextImpl extends ProcessingContext implements SerializationContext { private static final Logger LOGGER = Logger.getLogger(SerializationContextImpl.class.getName()); /** * Used to avoid StackOverflowError, when adapted / serialized object * contains instance of its type inside it or when object has recursive reference. */ private final Set currentlyProcessedObjects = new HashSet<>(); private final Type runtimeType; private String key = null; private boolean containerWithNulls = true; private boolean root = true; /** * Creates Marshaller for generation to String. * * @param jsonbContext Current context. * @param rootRuntimeType Type of root object. */ public SerializationContextImpl(JsonbContext jsonbContext, Type rootRuntimeType) { super(jsonbContext); this.runtimeType = rootRuntimeType; } /** * Creates Marshaller for generation to String. * * @param jsonbContext Current context. */ public SerializationContextImpl(JsonbContext jsonbContext) { this(jsonbContext, null); } /** * Set new current property key name. * * @param key key name */ public void setKey(String key) { this.key = key; } /** * Current property key name. * * @return current property key name */ public String getKey() { return key; } /** * Serialized value is a root value. * * @return is root value */ public boolean isRoot() { return root; } /** * Set whether serialized value is root value. * * @param root is root value */ public void setRoot(boolean root) { this.root = root; } /** * Value from this property is only used in {@link org.eclipse.yasson.internal.serializer.NullSerializer}. * It should not be used anywhere else. * * @return if container supports nulls */ public boolean isContainerWithNulls() { return containerWithNulls; } /** * Set if container supports null values. * * @param writeNulls should write nulls in container */ public void setContainerWithNulls(boolean writeNulls) { this.containerWithNulls = writeNulls; } /** * Marshals given object to provided Writer or OutputStream. * * @param object object to marshall * @param jsonGenerator generator to use * @param close if generator should be closed */ public void marshall(Object object, JsonGenerator jsonGenerator, boolean close) { try { serializeObject(object, jsonGenerator); } catch (JsonbException e) { throw e; } catch (RuntimeException e) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e); } finally { try { if (close) { jsonGenerator.close(); } else { jsonGenerator.flush(); } } catch (JsonGenerationException jge) { LOGGER.severe(jge.getMessage()); } } } /** * Marshals given object to provided Writer or OutputStream. * Closes the generator on completion. * * @param object object to marshall * @param jsonGenerator generator to use */ public void marshall(Object object, JsonGenerator jsonGenerator) { marshall(object, jsonGenerator, true); } /** * Marshals given object to provided Writer or OutputStream. * Leaves generator open for further interaction after completion. * * @param object object to marshall * @param jsonGenerator generator to use */ public void marshallWithoutClose(Object object, JsonGenerator jsonGenerator) { marshall(object, jsonGenerator, false); } @Override public void serialize(String key, T object, JsonGenerator generator) { Objects.requireNonNull(key); Objects.requireNonNull(object); setKey(key); serializeObject(object, generator); } @Override public void serialize(T object, JsonGenerator generator) { Objects.requireNonNull(object); serializeObject(object, generator); } /** * Serializes root element. * * @param Root type * @param root Root. * @param generator JSON generator. */ public void serializeObject(T root, JsonGenerator generator) { Type type = determineSerializationType(root); final ModelSerializer rootSerializer = getRootSerializer(type); rootSerializer.serialize(root, generator, this); } private Type determineSerializationType(T root) { if (isRoot() && runtimeType != null) { return runtimeType; } return root == null ? Object.class : root.getClass(); } public ModelSerializer getRootSerializer(Type type) { return getJsonbContext().getSerializationModelCreator().serializerChain(type, true, true); } /** * Adds currently processed object to the {@link Set}. * * @param object processed object * @return if object was added */ public boolean addProcessedObject(Object object) { return this.currentlyProcessedObjects.add(object); } /** * Removes processed object from the {@link Set}. * * @param object processed object * @return if object was removed */ public boolean removeProcessedObject(Object object) { return currentlyProcessedObjects.remove(object); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayDeque; import java.util.Deque; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Search for type variable in inheritance hierarchy and resolve if possible. */ class VariableTypeInheritanceSearch { private final Deque parameterizedSubclasses = new ArrayDeque<>(); /** * Searches the hierarchy of classes to resolve a type variable. If typevar resolved value is another typevar redirection * (propagated from wrapping class), * this typevar is returned. * *
     *
     * Example 1: typevar is resolved
     *
     *
     * class GenericClass <T> {
     *     private T genericField;
     * }
     * class ConcreteClass extends GenericClass<MyPojo> {
     *     //...
     * }
     *
     * In above case when ConcreteClass type is passed as runtime type and <T> as type variable, T is resolved to MyPojo.
     * 
* * *
     * Example 2: typevar is resolved to another propagated typevar
     *
     *
     * class WrapperGenericClass<X> {
     *     private GenericClass<X> propagatedGenericField
     * }
     *
     * class AnotherClass extends WrapperGenericClass<MyPojo> {
     * }
     *
     *
     * In second case when GenericClass {@link ParameterizedType} is passed as runtime type and <T> as type variable,
     * T is resolved to propagated <X> by WrapperGenericClass.
     *
     * Resolution on <X> must be performed thereafter with AnotherClass runtime type.
     * 
* * @param typeToSearch runtime type to search for typevar in, not null * @param typeVar type variable to resolve, not null * @return resolved runtime type, or type variable */ Type searchParametrizedType(Type typeToSearch, TypeVariable typeVar) { ParameterizedType parameterizedType = findParameterizedSuperclass(typeToSearch); if (parameterizedType == null) { return null; } Type matchedGenericType = searchRuntimeTypeArgument(parameterizedType, typeVar); if (matchedGenericType != null) { return matchedGenericType; } parameterizedSubclasses.push(parameterizedType); return searchParametrizedType(((Class) parameterizedType.getRawType()).getGenericSuperclass(), typeVar); } private Type checkSubclassRuntimeInfo(TypeVariable typeVar) { if (parameterizedSubclasses.size() == 0) { return typeVar; } ParameterizedType parametrizedSubclass = parameterizedSubclasses.pop(); return searchRuntimeTypeArgument(parametrizedSubclass, typeVar); } private Type searchRuntimeTypeArgument(ParameterizedType runtimeType, TypeVariable typeVar) { if (ReflectionUtils.getRawType(runtimeType) != typeVar.getGenericDeclaration()) { return null; } TypeVariable[] bounds = typeVar.getGenericDeclaration().getTypeParameters(); for (int i = 0; i < bounds.length; i++) { if (bounds[i].equals(typeVar)) { Type matchedGenericType = runtimeType.getActualTypeArguments()[i]; //Propagated generic types to another generic classes if (matchedGenericType instanceof TypeVariable) { return checkSubclassRuntimeInfo((TypeVariable) matchedGenericType); } //found runtime matchedGenericType return matchedGenericType; } } return null; } private static ParameterizedType findParameterizedSuperclass(Type type) { if (type == null || type instanceof ParameterizedType) { return (ParameterizedType) type; } if (!(type instanceof Class)) { throw new JsonbException(Messages.getMessage(MessageKeys.RESOLVE_PARAMETRIZED_TYPE, type)); } return findParameterizedSuperclass(((Class) type).getGenericSuperclass()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; import java.util.Objects; /** * Wrapper for user components, components, (de)serializer. * Contains resolved binding type an component. */ public abstract class AbstractComponentBinding { private final Type bindingType; /** * Creates info. * * @param bindingType type to which component is bound. */ public AbstractComponentBinding(Type bindingType) { Objects.requireNonNull(bindingType); this.bindingType = bindingType; } /** * Resolved binding type of a component. * * @return binding type */ public Type getBindingType() { return bindingType; } /** * Class of user component. * * @return component class */ public abstract Class getComponentClass(); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; import java.util.Objects; import jakarta.json.bind.adapter.JsonbAdapter; /** * Wrapper for JsonbAdapter generic information and an components itself. */ public class AdapterBinding extends AbstractComponentBinding { private final Type toType; private final JsonbAdapter adapter; /** * Adapter info with type to "adapt from", type to "adapt to" and an components itself. * * @param fromType from not null * @param toType to not null * @param adapter components not null */ public AdapterBinding(Type fromType, Type toType, JsonbAdapter adapter) { super(fromType); Objects.requireNonNull(toType); Objects.requireNonNull(adapter); this.toType = toType; this.adapter = adapter; } /** * Represents a type to which to adapt into. * * During marshalling object property is adapted to this type and result is marshalled. * During unmarshalling object is unmarshalled into this type first, than converted to field type and set. * * @return Type from which to adapt */ public Type getToType() { return toType; } /** * Get actual components to adapt object value. * * @return components */ public JsonbAdapter getAdapter() { return adapter; } @Override public Class getComponentClass() { return adapter.getClass(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.AnnotatedType; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.InjectionTarget; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.JsonBinding; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.eclipse.yasson.spi.JsonbComponentInstanceCreator; /** * CDI instance manager. * Instances are created and stored per instance of {@link JsonBinding}. * Calling close on JsonBinding, cleans up Jsonb CDI instances and in case of "dependant" scope its dependencies. * * CDI API dependency is optional, this class is never referenced / loaded if CDI API is not resolvable. */ public class BeanManagerInstanceCreator implements JsonbComponentInstanceCreator { private final BeanManager beanManager; private final ConcurrentMap, CDIManagedBean> injectionTargets = new ConcurrentHashMap<>(); /** * Creates a new instance. * * @param beanManager Bean manager. */ public BeanManagerInstanceCreator(Object beanManager) { if (!(beanManager instanceof BeanManager)) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "beanManager instance should be of type '" + BeanManager.class + "'")); } this.beanManager = (BeanManager) beanManager; } /** * Creates an instance of the CDI managed bean. * Calls CDI API to inject into the bean. * * @param componentClass bean class to be instantiated. * @return New instance of bean class with injected content. */ @Override @SuppressWarnings("unchecked") public T getOrCreateComponent(Class componentClass) { return (T) injectionTargets.computeIfAbsent(componentClass, clazz -> { final AnnotatedType aType = beanManager.createAnnotatedType(componentClass); final InjectionTarget injectionTarget = beanManager.getInjectionTargetFactory(aType) .createInjectionTarget(null); CreationalContext creationalContext = beanManager.createCreationalContext(null); final T beanInstance = injectionTarget.produce(creationalContext); injectionTarget.inject(beanInstance, creationalContext); injectionTarget.postConstruct(beanInstance); return new CDIManagedBean(beanInstance, injectionTarget, creationalContext); }).getInstance(); } @Override public void close() throws IOException { injectionTargets.forEach((clazz, target) -> cleanupBean(target)); injectionTargets.clear(); } private void cleanupBean(CDIManagedBean bean) { bean.getInjectionTarget().preDestroy(bean.getInstance()); bean.getInjectionTarget().dispose(bean.getInstance()); bean.getCreationalContext().release(); } /** * Holder for bean instance and its injection target. */ private static final class CDIManagedBean { private final T instance; private final InjectionTarget injectionTarget; private final CreationalContext creationalContext; CDIManagedBean(T instance, InjectionTarget injectionTarget, CreationalContext creationalContext) { this.instance = instance; this.injectionTarget = injectionTarget; this.creationalContext = creationalContext; } /** * @return CDI InjectionTarget */ private InjectionTarget getInjectionTarget() { return injectionTarget; } /** * @return managed instance of a bean */ private T getInstance() { return instance; } /** * @return creational context */ private CreationalContext getCreationalContext() { return creationalContext; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; /** * Wrapper holding singleton instances of user defined components - Adapters, (De)Serializers. */ public class ComponentBindings { private final Type bindingType; private final SerializerBinding serializer; private final DeserializerBinding deserializer; private final AdapterBinding adapterInfo; /** * Construct empty bindings for a given type. * * @param bindingType type components are bound to */ public ComponentBindings(Type bindingType) { this(bindingType, null, null, null); } /** * Creates an instance and populates it with bindings for a given type. * * @param bindingType Type components are bound to. * @param serializer Serializer. * @param deserializer Deserializer. * @param adapter Adapter. */ public ComponentBindings(Type bindingType, SerializerBinding serializer, DeserializerBinding deserializer, AdapterBinding adapter) { this.bindingType = bindingType; this.serializer = serializer; this.deserializer = deserializer; this.adapterInfo = adapter; } /** * Type to which components are bound. * * @return Bound type. */ public Type getBindingType() { return bindingType; } /** * Serializer if any. * * @return serializer */ public SerializerBinding getSerializer() { return serializer; } /** * Deserializer if any. * * @return deserializer */ public DeserializerBinding getDeserializer() { return deserializer; } /** * Adapter info if any. * * @return adapterInfo */ public AdapterBinding getAdapterInfo() { return adapterInfo; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/DefaultConstructorCreator.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.io.IOException; import org.eclipse.yasson.internal.InstanceCreator; import org.eclipse.yasson.spi.JsonbComponentInstanceCreator; /** * Creates components instance with default constructor. */ public class DefaultConstructorCreator implements JsonbComponentInstanceCreator { @Override public T getOrCreateComponent(Class componentClass) { return InstanceCreator.createInstance(componentClass); } @Override public void close() throws IOException { } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; import jakarta.json.bind.serializer.JsonbDeserializer; /** * Component containing deserializer. * * @param type of contained deserializer */ public class DeserializerBinding extends AbstractComponentBinding { private final JsonbDeserializer jsonbDeserializer; /** * Creates a new instance. * * @param bindingType Binding type. * @param jsonbDeserializer Deserializer. */ public DeserializerBinding(Type bindingType, JsonbDeserializer jsonbDeserializer) { super(bindingType); this.jsonbDeserializer = jsonbDeserializer; } /** * Gets deserializer if any. * * @return Deserializer. */ public JsonbDeserializer getJsonbDeserializer() { return jsonbDeserializer; } @Override public Class getComponentClass() { return jsonbDeserializer.getClass(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/JsonbComponentInstanceCreatorFactory.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.logging.Level; import java.util.logging.Logger; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.eclipse.yasson.spi.JsonbComponentInstanceCreator; /** * Factory method for default Jsonb component instance creators. */ public class JsonbComponentInstanceCreatorFactory { private static final Logger LOGGER = Logger.getLogger(JsonbComponentInstanceCreator.class.getName()); private JsonbComponentInstanceCreatorFactory() { throw new IllegalStateException("This class should never be instantiated"); } /** * JNDI bean manager name. */ public static final String BEAN_MANAGER_NAME = "java:comp/BeanManager"; /** * Initial context class. */ public static final String INITIAL_CONTEXT_CLASS = "javax.naming.InitialContext"; private static final String CDI_SPI_CLASS = "jakarta.enterprise.inject.spi.CDI"; /** * First check a CDI provider, if available use those. * Try to lookup in a JNDI if no provider is registered. * If one of the above is found {@link BeanManagerInstanceCreator} is returned, * or {@link DefaultConstructorCreator} otherwise. * * @return Component instance creator, either CDI or default constructor. */ public static JsonbComponentInstanceCreator getComponentInstanceCreator() { Object beanManager = getCdiBeanManager(); if (beanManager == null) { beanManager = getJndiBeanManager(); } if (beanManager == null) { LOGGER.finest(Messages.getMessage(MessageKeys.BEAN_MANAGER_NOT_FOUND_USING_DEFAULT)); return new DefaultConstructorCreator(); } return new BeanManagerInstanceCreator(beanManager); } /** * Get bean manager with CDI api. * * @return bean manager instance or null if CDI API dependency is not available. */ private static Object getCdiBeanManager() { return AccessController.doPrivileged((PrivilegedAction) () -> { try { return getBeanManager(() -> { Class cdiClass = Class.forName(CDI_SPI_CLASS); Method current = cdiClass.getMethod("current"); Method getBeanManager = cdiClass.getMethod("getBeanManager"); Object cdiObject = current.invoke(cdiClass); if (cdiObject == null) { return null; } return getBeanManager.invoke(cdiObject); }); } catch (ClassNotFoundException e) { LOGGER.finest(Messages.getMessage(MessageKeys.NO_CDI_API_PROVIDER, CDI_SPI_CLASS)); return null; } }); } /** * Get bean manager from JNDI context. * * @return bean manager instance or null if javax.naming is not available. */ private static Object getJndiBeanManager() { return AccessController.doPrivileged((PrivilegedAction) () -> { try { return getBeanManager(() -> { Class initialContextClass = Class.forName(INITIAL_CONTEXT_CLASS); Method lookupMethod = initialContextClass.getMethod("lookup", String.class); Constructor initialContextConstructor = initialContextClass.getConstructor(); Object initialContextObject = initialContextConstructor.newInstance(); return lookupMethod.invoke(initialContextObject, BEAN_MANAGER_NAME); }); } catch (ClassNotFoundException e) { LOGGER.finest(Messages.getMessage(MessageKeys.NO_JNDI_ENVIRONMENT, INITIAL_CONTEXT_CLASS)); return null; } }); } /** * Handles common invocation exceptions for getting bean manager reflectively. * * @return bean manager instance or null if javax.naming is not available or insufficient permissions. */ private static Object getBeanManager(BeanManagerProvider command) throws ClassNotFoundException { try { return command.provide(); } catch (NoSuchMethodException | InstantiationException e) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e); } catch (IllegalAccessException e) { //insufficient permissions for reflective invocation, don't fail in this case LOGGER.warning(e.getMessage()); LOGGER.warning(Messages.getMessage(MessageKeys.ILLEGAL_ACCESS, "lookup CDI bean manager")); return null; } catch (ClassNotFoundException e) { throw e; } catch (ReflectiveOperationException e) { //likely no CDI container is running or bean manager JNDI lookup fails. LOGGER.log(Level.FINEST, Messages.getMessage(MessageKeys.NO_CDI_ENVIRONMENT), e); return null; } } /** * Provides CDI bean manager instance, declares all exceptions thrown with reflective calls. */ private interface BeanManagerProvider { Object provide() throws ReflectiveOperationException; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.components; import java.lang.reflect.Type; import jakarta.json.bind.serializer.JsonbSerializer; /** * Binding for user Serializer component. * * @param type of jsonb serializer */ public class SerializerBinding extends AbstractComponentBinding { private final JsonbSerializer jsonbSerializer; /** * Creates a new instance. * * @param bindingType Generic type argument of serializer. Not null. * @param jsonbSerializer Serializer. Can be null. */ public SerializerBinding(Type bindingType, JsonbSerializer jsonbSerializer) { super(bindingType); this.jsonbSerializer = jsonbSerializer; } /** * Returns a serializer if any. * * @return Serializer. */ public JsonbSerializer getJsonbSerializer() { return jsonbSerializer; } /** * Class of user component. * * @return Component class. */ @Override public Class getComponentClass() { return jsonbSerializer.getClass(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import jakarta.json.bind.JsonbException; import jakarta.json.bind.adapter.JsonbAdapter; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * User defined type adapter executor. */ class AdapterDeserializer implements ModelDeserializer { private final JsonbAdapter adapter; private final AdapterBinding adapterBinding; private final ModelDeserializer delegate; @SuppressWarnings("unchecked") AdapterDeserializer(AdapterBinding adapterBinding, ModelDeserializer delegate) { this.adapterBinding = adapterBinding; this.adapter = (JsonbAdapter) adapterBinding.getAdapter(); this.delegate = delegate; } @Override public Object deserialize(Object value, DeserializationContextImpl context) { try { return delegate.deserialize(adapter.adaptFromJson(value), context); } catch (Exception e) { throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION, adapterBinding.getBindingType(), adapterBinding.getToType(), adapterBinding.getAdapter().getClass()), e); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ArrayDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.ArrayList; import java.util.Collection; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Array container deserializer. */ class ArrayDeserializer implements ModelDeserializer { private final ModelDeserializer delegate; ArrayDeserializer(ModelDeserializer delegate) { this.delegate = delegate; } @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { Collection collection = new ArrayList<>(); while (parser.hasNext()) { final JsonParser.Event next = parser.next(); context.setLastValueEvent(next); switch (next) { case START_OBJECT: case START_ARRAY: case VALUE_STRING: case VALUE_TRUE: case VALUE_FALSE: case VALUE_NUMBER: case VALUE_NULL: DeserializationContextImpl newContext = new DeserializationContextImpl(context); collection.add(delegate.deserialize(parser, newContext)); break; case END_ARRAY: return collection; default: throw new JsonbException("Unexpected state: " + next); } } return collection; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ArrayInstanceCreator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.Array; import java.util.Base64; import java.util.Collection; import java.util.Map; import java.util.function.Function; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.BinaryDataStrategy; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Creator of the array instance based upon the array type. */ abstract class ArrayInstanceCreator implements ModelDeserializer { private static final Map, Function, ArrayInstanceCreator>> CACHE; static { CACHE = Map.of(boolean[].class, BooleanArrayCreator::new, byte[].class, ByteArrayCreator::new, char[].class, CharArrayCreator::new, double[].class, DoubleArrayCreator::new, float[].class, FloatArrayCreator::new, int[].class, IntegerArrayCreator::new, long[].class, LongArrayCreator::new, short[].class, ShortArrayCreator::new); } private final ModelDeserializer delegate; private ArrayInstanceCreator(ModelDeserializer delegate) { this.delegate = delegate; } static ArrayInstanceCreator create(Class arrayType, Class componentClass, ModelDeserializer delegate) { if (CACHE.containsKey(arrayType)) { return CACHE.get(arrayType).apply(delegate); } return new ObjectArrayCreator(delegate, componentClass); } static ModelDeserializer createBase64Deserializer(String strategy, ModelDeserializer delegate) { return new Base64ByteArray(strategy, delegate); } @SuppressWarnings("unchecked") @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Collection collection = (Collection) delegate.deserialize(value, context); return resolveArrayInstance(collection); } protected abstract Object resolveArrayInstance(Collection collection); private static final class IntegerArrayCreator extends ArrayInstanceCreator { private IntegerArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { int[] intArray = new int[collection.size()]; int i = 0; for (Object obj : collection) { intArray[i] = (int) obj; i++; } return intArray; } } private static final class ByteArrayCreator extends ArrayInstanceCreator { private ByteArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { byte[] byteArray = new byte[collection.size()]; int i = 0; for (Object obj : collection) { byteArray[i] = (byte) obj; i++; } return byteArray; } } private static final class ShortArrayCreator extends ArrayInstanceCreator { private ShortArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { short[] shortArray = new short[collection.size()]; int i = 0; for (Object obj : collection) { shortArray[i] = (short) obj; i++; } return shortArray; } } private static final class LongArrayCreator extends ArrayInstanceCreator { private LongArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { long[] longArray = new long[collection.size()]; int i = 0; for (Object obj : collection) { longArray[i] = (long) obj; i++; } return longArray; } } private static final class FloatArrayCreator extends ArrayInstanceCreator { private FloatArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { float[] floatArray = new float[collection.size()]; int i = 0; for (Object obj : collection) { floatArray[i] = (float) obj; i++; } return floatArray; } } private static final class DoubleArrayCreator extends ArrayInstanceCreator { private DoubleArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { double[] doubleArray = new double[collection.size()]; int i = 0; for (Object obj : collection) { doubleArray[i] = (double) obj; i++; } return doubleArray; } } private static final class BooleanArrayCreator extends ArrayInstanceCreator { private BooleanArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { boolean[] booleanArray = new boolean[collection.size()]; int i = 0; for (Object obj : collection) { booleanArray[i] = (boolean) obj; i++; } return booleanArray; } } private static final class CharArrayCreator extends ArrayInstanceCreator { private CharArrayCreator(ModelDeserializer delegate) { super(delegate); } @Override protected Object resolveArrayInstance(Collection collection) { char[] charArray = new char[collection.size()]; int i = 0; for (Object obj : collection) { charArray[i] = (char) obj; i++; } return charArray; } } private static final class ObjectArrayCreator extends ArrayInstanceCreator { private final Class componentClass; private ObjectArrayCreator(ModelDeserializer delegate, Class componentClass) { super(delegate); this.componentClass = componentClass; } @Override protected Object resolveArrayInstance(Collection collection) { Object[] objectArray = (Object[]) Array.newInstance(componentClass, collection.size()); int i = 0; for (Object obj : collection) { objectArray[i] = obj; i++; } return objectArray; } } private static final class Base64ByteArray implements ModelDeserializer { private final Base64.Decoder decoder; private final ModelDeserializer delegate; private Base64ByteArray(String strategy, ModelDeserializer delegate) { this.decoder = getDecoder(strategy); this.delegate = delegate; } public Base64.Decoder getDecoder(String strategy) { switch (strategy) { case BinaryDataStrategy.BASE_64: return Base64.getDecoder(); case BinaryDataStrategy.BASE_64_URL: return Base64.getUrlDecoder(); default: throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Invalid strategy: " + strategy)); } } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { return decoder.decode((String) delegate.deserialize(value, context)); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/CollectionDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.Collection; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Collection container deserializer. */ class CollectionDeserializer implements ModelDeserializer { private final ModelDeserializer delegate; CollectionDeserializer(ModelDeserializer delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { Collection collection = (Collection) context.getInstance(); while (parser.hasNext()) { final JsonParser.Event next = parser.next(); context.setLastValueEvent(next); switch (next) { case VALUE_NULL: case START_OBJECT: case START_ARRAY: case VALUE_STRING: case VALUE_TRUE: case VALUE_FALSE: case VALUE_NUMBER: DeserializationContextImpl newContext = new DeserializationContextImpl(context); collection.add(delegate.deserialize(parser, newContext)); break; case END_ARRAY: return collection; default: throw new JsonbException("Unexpected state: " + next); } } return collection; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/CollectionInstanceCreator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.InstanceCreator; import org.eclipse.yasson.internal.ReflectionUtils; /** * Collection instance creator. */ class CollectionInstanceCreator implements ModelDeserializer { private final CollectionDeserializer delegate; private final Type type; private final Class clazz; private final boolean isEnumSet; CollectionInstanceCreator(CollectionDeserializer delegate, Type type) { this.delegate = delegate; this.clazz = implementationClass(ReflectionUtils.getRawType(type)); this.isEnumSet = EnumSet.class.isAssignableFrom(clazz); this.type = isEnumSet ? ((ParameterizedType) type).getActualTypeArguments()[0] : type; } @SuppressWarnings("unchecked") @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Object instance; if (isEnumSet) { instance = EnumSet.noneOf((Class) type); } else { instance = InstanceCreator.createInstance(clazz); } context.setInstance(instance); return delegate.deserialize(value, context); } private Class implementationClass(Class type) { if (type.isInterface()) { return createInterfaceInstance(type); } return type; } private Class createInterfaceInstance(Class ifcType) { if (List.class.isAssignableFrom(ifcType)) { return ArrayList.class; } if (Set.class.isAssignableFrom(ifcType)) { if (SortedSet.class.isAssignableFrom(ifcType)) { return TreeSet.class; } return HashSet.class; } if (Queue.class.isAssignableFrom(ifcType)) { return ArrayDeque.class; } if (Collection.class == ifcType) { return ArrayList.class; } return ifcType; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ContextSwitcher.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer which creates new deserialization context and invokes delegate with it. */ class ContextSwitcher implements ModelDeserializer { private final ModelDeserializer delegate; private final ModelDeserializer modelDeserializer; ContextSwitcher(ModelDeserializer delegate, ModelDeserializer modelDeserializer) { this.delegate = delegate; this.modelDeserializer = modelDeserializer; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { DeserializationContextImpl ctx = new DeserializationContextImpl(context); Object returnedValue = delegate.deserialize(modelDeserializer.deserialize(value, ctx), context); context.setLastValueEvent(ctx.getLastValueEvent()); return returnedValue; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/CyclicReferenceDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.Type; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserialization solution for cyclic references. */ class CyclicReferenceDeserializer implements ModelDeserializer { private final Type type; private ModelDeserializer delegate; CyclicReferenceDeserializer(Type type) { this.type = type; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (delegate == null) { delegate = context.getJsonbContext().getChainModelCreator().deserializerChain(type); } return delegate.deserialize(value, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/DefaultObjectInstanceCreator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.Constructor; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.ClassMultiReleaseExtension; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.ReflectionUtils; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Creator of the class instance with the default constructor. */ class DefaultObjectInstanceCreator implements ModelDeserializer { private final ModelDeserializer delegate; private final Constructor defaultConstructor; private final JsonbException exception; DefaultObjectInstanceCreator(ModelDeserializer delegate, Class clazz, Constructor defaultConstructor) { this.delegate = delegate; this.defaultConstructor = defaultConstructor; if (clazz.isInterface()) { this.exception = new JsonbException(Messages.getMessage(MessageKeys.INFER_TYPE_FOR_UNMARSHALL, clazz.getName())); } else if (defaultConstructor == null) { this.exception = ClassMultiReleaseExtension.exceptionToThrow(clazz) .orElse(new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, clazz))); } else { this.exception = null; } } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (exception != null) { throw exception; } Object instance = ReflectionUtils.createNoArgConstructorInstance(defaultConstructor); context.setInstance(instance); return delegate.deserialize(value, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/DeferredDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deferred deserializer used for postponed value setting. Such as when {@link jakarta.json.bind.annotation.JsonbCreator} * is used. */ class DeferredDeserializer implements ModelDeserializer { private final ModelDeserializer delegate; DeferredDeserializer(ModelDeserializer delegate) { this.delegate = delegate; } @Override public Object deserialize(Object value, DeserializationContextImpl context) { context.getDeferredDeserializers().add(() -> delegate.deserialize(value, context)); return value; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.BinaryDataStrategy; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.ReflectionUtils; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.deserializer.types.TypeDeserializers; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.model.PropertyModel; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.model.customization.PropertyCustomization; import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import static jakarta.json.bind.JsonbConfig.PROPERTY_NAMING_STRATEGY; import static jakarta.json.stream.JsonParser.Event; /** * Creator of the deserialization models for deserialized types. *
* This class servers also as a cache for all previously created model deserializers. */ public class DeserializationModelCreator { private static final ModelDeserializer NULL_PROVIDER = (value, context) -> null; private static final Map, ModelDeserializer> DEFAULT_CREATOR_VALUES; private static final Set MAP_KEY_EVENTS = new HashSet<>(); static { MAP_KEY_EVENTS.add(Event.KEY_NAME); MAP_KEY_EVENTS.addAll(PositionChecker.Checker.VALUES.getEvents()); Map, ModelDeserializer> tmpValuesMap = new HashMap<>(); tmpValuesMap.put(byte.class, (value, context) -> (byte) 0); tmpValuesMap.put(short.class, (value, context) -> (short) 0); tmpValuesMap.put(int.class, (value, context) -> 0); tmpValuesMap.put(long.class, (value, context) -> 0L); tmpValuesMap.put(float.class, (value, context) -> 0.0F); tmpValuesMap.put(double.class, (value, context) -> 0.0); tmpValuesMap.put(char.class, (value, context) -> '\u0000'); tmpValuesMap.put(boolean.class, (value, context) -> false); tmpValuesMap.put(Optional.class, (value, context) -> Optional.empty()); tmpValuesMap.put(OptionalInt.class, (value, context) -> OptionalInt.empty()); tmpValuesMap.put(OptionalLong.class, (value, context) -> OptionalLong.empty()); tmpValuesMap.put(OptionalDouble.class, (value, context) -> OptionalDouble.empty()); DEFAULT_CREATOR_VALUES = Map.copyOf(tmpValuesMap); } private final Map> models = new ConcurrentHashMap<>(); private final JsonbContext jsonbContext; private final Map, Class> userTypeMapping; /** * Create new instance. * * @param jsonbContext jsonb context */ public DeserializationModelCreator(JsonbContext jsonbContext) { this.jsonbContext = jsonbContext; this.userTypeMapping = jsonbContext.getConfigProperties().getUserTypeMapping(); } /** * Starts deserializer creation process. * * @param type type the deserializer is created for * @return created deserializer */ public ModelDeserializer deserializerChain(Type type) { LinkedList chain = new LinkedList<>(); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(type)); return deserializerChain(chain, type, classModel.getClassCustomization(), classModel); } private ModelDeserializer deserializerChain(LinkedList chain, Type type, Customization propertyCustomization, ClassModel classModel) { if (chain.contains(type)) { return new CyclicReferenceDeserializer(type); } try { chain.add(type); return deserializerChainInternal(chain, type, propertyCustomization, classModel); } finally { chain.removeLast(); } } private ModelDeserializer deserializerChainInternal(LinkedList chain, Type type, Customization propertyCustomization, ClassModel classModel) { Class rawType = classModel.getType(); CachedItem cachedItem = createCachedItem(type, propertyCustomization); if (models.containsKey(cachedItem)) { return models.get(cachedItem); } else if (userTypeMapping.containsKey(rawType)) { Class userTypeRaw = userTypeMapping.get(rawType); ModelDeserializer deserializer = deserializerChain(userTypeRaw); models.put(cachedItem, deserializer); return deserializer; } Optional adapterBinding = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization); if (adapterBinding.isPresent()) { AdapterBinding adapter = adapterBinding.get(); Class toType = ReflectionUtils.getRawType(adapter.getToType()); ClassModel targetModel = jsonbContext.getMappingContext().getOrCreateClassModel(toType); ModelDeserializer typeDeserializer = typeDeserializer(toType, targetModel.getClassCustomization(), JustReturn.instance()); if (typeDeserializer == null) { typeDeserializer = deserializerChain(adapter.getToType()); } ModelDeserializer targetAdapterModel = typeDeserializer; AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, JustReturn.instance()); ModelDeserializer adapterDeser = (parser, context) -> { Object fromJson = targetAdapterModel.deserialize(parser, context); return adapterDeserializer.deserialize(fromJson, context); }; models.put(cachedItem, adapterDeser); return adapterDeser; } ModelDeserializer typeDeserializer = typeDeserializer(rawType, propertyCustomization, JustReturn.instance()); if (typeDeserializer != null) { models.put(cachedItem, typeDeserializer); return typeDeserializer; } if (Collection.class.isAssignableFrom(rawType)) { return createCollectionDeserializer(cachedItem, rawType, chain); } else if (Map.class.isAssignableFrom(rawType)) { return createMapDeserializer(cachedItem, rawType, chain); } else if (rawType.isArray()) { return createArrayDeserializer(cachedItem, rawType, chain); } else if (type instanceof GenericArrayType) { return createGenericArray(cachedItem, rawType, chain); } else if (Optional.class.isAssignableFrom(rawType)) { return createOptionalDeserializer(chain, type, cachedItem); } else { return createObjectDeserializer(chain, type, propertyCustomization, classModel, rawType, cachedItem); } } private ModelDeserializer createObjectDeserializer(LinkedList chain, Type type, Customization propertyCustomization, ClassModel classModel, Class rawType, CachedItem cachedItem) { ClassCustomization classCustomization = classModel.getClassCustomization(); Optional> deserializerBinding = userDeserializer(type, (ComponentBoundCustomization) propertyCustomization); if (deserializerBinding.isPresent()) { UserDefinedDeserializer user = new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), JustReturn.instance(), type, classCustomization); models.put(cachedItem, user); return user; } JsonbCreator creator = classCustomization.getCreator(); boolean hasCreator = creator != null; List params = hasCreator ? creatorParamsList(creator) : Collections.emptyList(); Function renamer = propertyRenamer(); Map> processors = new LinkedHashMap<>(); Map> defaultCreatorValues = new HashMap<>(); for (PropertyModel propertyModel : classModel.getSortedProperties()) { if (!propertyModel.isWritable() || params.contains(propertyModel.getReadName())) { continue; } ModelDeserializer modelDeserializer = memberTypeProcessor(chain, propertyModel, hasCreator); processors.put(renamer.apply(propertyModel.getReadName()), modelDeserializer); } for (String s : params) { CreatorModel creatorModel = creator.findByName(s); ModelDeserializer modelDeserializer = typeProcessor(chain, creatorModel.getType(), creatorModel.getCustomization(), JustReturn.instance()); String parameterName = renamer.apply(creatorModel.getName()); processors.put(parameterName, modelDeserializer); if (creatorModel.getCustomization().isRequired()) { defaultCreatorValues.put(parameterName, new RequiredCreatorParameter(parameterName)); } else { Class rawParamType = ReflectionUtils.getOptionalRawType(creatorModel.getType()).orElse(Object.class); defaultCreatorValues.put(parameterName, DEFAULT_CREATOR_VALUES.getOrDefault(rawParamType, NULL_PROVIDER)); } } ModelDeserializer instanceCreator; TypeInheritanceConfiguration typeInheritanceConfiguration = classCustomization.getPolymorphismConfig(); Set ignoredProperties = collectIgnoredProperties(typeInheritanceConfiguration); boolean failOnUnknownProperties = jsonbContext.getConfigProperties().getConfigFailOnUnknownProperties(); if (hasCreator) { instanceCreator = new JsonbCreatorDeserializer(processors, defaultCreatorValues, creator, rawType, renamer, failOnUnknownProperties, ignoredProperties); } else { ModelDeserializer typeWrapper = new ObjectDeserializer(processors, renamer, rawType, failOnUnknownProperties, ignoredProperties); instanceCreator = new DefaultObjectInstanceCreator(typeWrapper, rawType, classModel.getDefaultConstructor()); } PositionChecker positionChecker = new PositionChecker(instanceCreator, rawType, Event.START_OBJECT); if (typeInheritanceConfiguration != null && !typeInheritanceConfiguration.isInherited()) { instanceCreator = new InheritanceInstanceCreator(rawType, this, typeInheritanceConfiguration, positionChecker); positionChecker = new PositionChecker(instanceCreator, rawType, Event.START_OBJECT); } ModelDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); models.put(cachedItem, nullChecker); return nullChecker; } private ModelDeserializer createCollectionDeserializer(CachedItem cachedItem, Class rawType, LinkedList chain) { Type type = cachedItem.type; Type colType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; colType = ReflectionUtils.resolveType(chain, colType); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(colType)); ModelDeserializer typeProcessor = typeProcessor(chain, colType, classModel.getClassCustomization(), JustReturn.instance()); CollectionDeserializer collectionDeserializer = new CollectionDeserializer(typeProcessor); CollectionInstanceCreator instanceDeserializer = new CollectionInstanceCreator(collectionDeserializer, type); PositionChecker positionChecker = new PositionChecker(instanceDeserializer, rawType, Event.START_ARRAY); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); models.put(cachedItem, nullChecker); return nullChecker; } private ModelDeserializer createMapDeserializer(CachedItem cachedItem, Class rawType, LinkedList chain) { Type type = cachedItem.type; Type keyType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; Type valueType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[1] : Object.class; ModelDeserializer keyProcessor = typeProcessor(chain, keyType, ClassCustomization.empty(), JustReturn.instance(), MAP_KEY_EVENTS); ClassModel valueClassModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.resolveRawType(chain, valueType)); ModelDeserializer valueProcessor = typeProcessor(chain, valueType, valueClassModel.getClassCustomization(), JustReturn.instance()); MapDeserializer mapDeserializer = new MapDeserializer(keyProcessor, valueProcessor); MapInstanceCreator mapInstanceCreator = new MapInstanceCreator(mapDeserializer, jsonbContext.getConfigProperties(), rawType); PositionChecker positionChecker = new PositionChecker(mapInstanceCreator, rawType, PositionChecker.Checker.CONTAINER); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); models.put(cachedItem, nullChecker); return nullChecker; } private ModelDeserializer createArrayDeserializer(CachedItem cachedItem, Class rawType, LinkedList chain) { JsonbConfigProperties configProperties = jsonbContext.getConfigProperties(); if (rawType.equals(byte[].class) && !configProperties.getBinaryDataStrategy().equals(BinaryDataStrategy.BYTE)) { String strategy = configProperties.getBinaryDataStrategy(); // Special case for byte[] with base64 encoding - use String's class customization ClassModel stringModel = jsonbContext.getMappingContext().getOrCreateClassModel(String.class); ModelDeserializer typeProcessor = typeProcessor(chain, String.class, stringModel.getClassCustomization(), JustReturn.instance()); ModelDeserializer base64Deserializer = ArrayInstanceCreator.createBase64Deserializer(strategy, typeProcessor); NullCheckDeserializer nullChecker = new NullCheckDeserializer(base64Deserializer, JustReturn.instance()); models.put(cachedItem, nullChecker); return nullChecker; } Class arrayType = rawType.getComponentType(); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(arrayType); ModelDeserializer typeProcessor = typeProcessor(chain, arrayType, classModel.getClassCustomization(), JustReturn.instance()); return createArrayCommonDeserializer(cachedItem, rawType, arrayType, typeProcessor); } private ModelDeserializer createGenericArray(CachedItem cachedItem, Class rawType, LinkedList chain) { GenericArrayType type = (GenericArrayType) cachedItem.type; Class component = ReflectionUtils.getRawType(type.getGenericComponentType()); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(component); ModelDeserializer typeProcessor = typeProcessor(chain, type.getGenericComponentType(), classModel.getClassCustomization(), JustReturn.instance()); return createArrayCommonDeserializer(cachedItem, rawType, component, typeProcessor); } private ModelDeserializer createArrayCommonDeserializer(CachedItem cachedItem, Class rawType, Class component, ModelDeserializer typeProcessor) { ArrayDeserializer arrayDeserializer = new ArrayDeserializer(typeProcessor); ArrayInstanceCreator arrayInstanceCreator = ArrayInstanceCreator.create(rawType, component, arrayDeserializer); PositionChecker positionChecker = new PositionChecker(arrayInstanceCreator, rawType, Event.START_ARRAY); NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance()); models.put(cachedItem, nullChecker); return nullChecker; } private OptionalDeserializer createOptionalDeserializer(LinkedList chain, Type type, CachedItem cachedItem) { Type colType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; colType = ReflectionUtils.resolveType(chain, colType); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(colType)); ModelDeserializer typeProcessor = typeProcessor(chain, colType, classModel.getClassCustomization(), JustReturn.instance()); OptionalDeserializer optionalDeserializer = new OptionalDeserializer(typeProcessor, JustReturn.instance()); models.put(cachedItem, optionalDeserializer); return optionalDeserializer; } private Set collectIgnoredProperties(TypeInheritanceConfiguration typeInheritanceConfiguration) { Set ignoredProperties = new HashSet<>(); if (typeInheritanceConfiguration != null) { TypeInheritanceConfiguration current = typeInheritanceConfiguration; while (current != null) { ignoredProperties.add(current.getFieldName()); current = current.getParentConfig(); } } return ignoredProperties; } private Function propertyRenamer() { boolean isCaseInsensitive = jsonbContext.getConfig() .getProperty(PROPERTY_NAMING_STRATEGY) .filter(prop -> prop.equals(PropertyNamingStrategy.CASE_INSENSITIVE)) .isPresent(); return isCaseInsensitive ? String::toLowerCase : value -> value; } private Optional adapterBinding(Type type, ComponentBoundCustomization classCustomization) { return jsonbContext.getComponentMatcher().getDeserializeAdapterBinding(type, classCustomization); } private Optional> userDeserializer(Type type, ComponentBoundCustomization classCustomization) { return jsonbContext.getComponentMatcher().getDeserializerBinding(type, classCustomization); } private List creatorParamsList(JsonbCreator creator) { return Arrays.stream(creator.getParams()).map(CreatorModel::getName).collect(Collectors.toList()); } private ModelDeserializer memberTypeProcessor(LinkedList chain, PropertyModel propertyModel, boolean hasCreator) { ModelDeserializer memberDeserializer; Type type = propertyModel.getPropertyDeserializationType(); memberDeserializer = new ValueSetterDeserializer(propertyModel.getSetValueHandle()); if (hasCreator) { memberDeserializer = new DeferredDeserializer(memberDeserializer); } return typeProcessor(chain, type, propertyModel.getCustomization(), memberDeserializer); } private ModelDeserializer typeProcessor(LinkedList chain, Type type, Customization customization, ModelDeserializer memberDeserializer) { return typeProcessor(chain, type, customization, memberDeserializer, PositionChecker.Checker.VALUES.getEvents()); } private ModelDeserializer typeProcessor(LinkedList chain, Type type, Customization customization, ModelDeserializer memberDeserializer, Set events) { Type resolved = ReflectionUtils.resolveType(chain, type); Class rawType = ReflectionUtils.getRawType(resolved); Optional> deserializerBinding = userDeserializer(resolved, (ComponentBoundCustomization) customization); if (deserializerBinding.isPresent()) { //TODO remove or not? fix for deserializer cycle // ModelDeserializer exactType = createNewChain(chain, memberDeserializer, rawType, // resolved, customization); // return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), // exactType, // memberDeserializer, // resolved, // customization); return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(), memberDeserializer, resolved, customization); } Optional adapterBinding = adapterBinding(resolved, (ComponentBoundCustomization) customization); if (adapterBinding.isPresent()) { AdapterBinding adapter = adapterBinding.get(); ModelDeserializer typeDeserializer = typeDeserializer(ReflectionUtils.getRawType(adapter.getToType()), customization, JustReturn.instance(), events); if (typeDeserializer == null) { typeDeserializer = deserializerChain(adapter.getToType()); } ModelDeserializer targetAdapterModel = typeDeserializer; AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, memberDeserializer); return (parser, context) -> { DeserializationContextImpl newContext = new DeserializationContextImpl(context); Object fromJson = targetAdapterModel.deserialize(parser, newContext); return adapterDeserializer.deserialize(fromJson, context); }; } ModelDeserializer typeDeserializer = typeDeserializer(rawType, customization, memberDeserializer, events); if (typeDeserializer == null) { Class implClass = resolveImplClass(rawType, customization); return createNewChain(chain, memberDeserializer, implClass, resolved, customization); } return typeDeserializer; } private ModelDeserializer createNewChain(LinkedList chain, ModelDeserializer memberDeserializer, Class rawType, Type type, Customization propertyCustomization) { ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType); ModelDeserializer modelDeserializer = deserializerChain(chain, type, propertyCustomization, classModel); return new ContextSwitcher(memberDeserializer, modelDeserializer); } private ModelDeserializer typeDeserializer(Class rawType, Customization customization, ModelDeserializer delegate) { return typeDeserializer(rawType, customization, delegate, PositionChecker.Checker.VALUES.getEvents()); } private ModelDeserializer typeDeserializer(Class rawType, Customization customization, ModelDeserializer delegate, Set events) { return TypeDeserializers .getTypeDeserializer(rawType, customization, jsonbContext.getConfigProperties(), delegate, events); } private Class resolveImplClass(Class rawType, Customization customization) { if (rawType.isInterface()) { Class implementationClass = null; //annotation if (customization instanceof PropertyCustomization) { implementationClass = ((PropertyCustomization) customization).getImplementationClass(); } //JsonbConfig if (implementationClass == null) { implementationClass = jsonbContext.getConfigProperties().getUserTypeMapping().get(rawType); } if (implementationClass != null) { if (!rawType.isAssignableFrom(implementationClass)) { throw new JsonbException(Messages.getMessage(MessageKeys.IMPL_CLASS_INCOMPATIBLE, implementationClass, rawType)); } return implementationClass; } } return rawType; } private CachedItem createCachedItem(Type type, Customization customization) { return new CachedItem(type, customization.getDeserializeNumberFormatter(), customization.getDeserializeDateFormatter()); } private static final class CachedItem { private final Type type; private final JsonbNumberFormatter numberFormatter; private final JsonbDateFormatter dateFormatter; CachedItem(Type type, JsonbNumberFormatter numberFormatter, JsonbDateFormatter dateFormatter) { this.type = type; this.numberFormatter = numberFormatter; this.dateFormatter = dateFormatter; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CachedItem that = (CachedItem) o; return Objects.equals(type, that.type) && Objects.equals(numberFormatter, that.numberFormatter) && Objects.equals(dateFormatter, that.dateFormatter); } @Override public int hashCode() { return Objects.hash(type, numberFormatter, dateFormatter); } @Override public String toString() { return "CachedItem{" + "type=" + type + ", numberFormatter=" + numberFormatter + ", dateFormatter=" + dateFormatter + '}'; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jakarta.json.JsonObject; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter; import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration; import static jakarta.json.stream.JsonParser.Event; /** * Instance creator following the inheritance structure defined by {@link jakarta.json.bind.annotation.JsonbTypeInfo}. */ class InheritanceInstanceCreator implements ModelDeserializer { private final Class processedType; private final Map> resolvedClasses = new ConcurrentHashMap<>(); private final DeserializationModelCreator deserializationModelCreator; private final TypeInheritanceConfiguration typeInheritanceConfiguration; private final ModelDeserializer defaultProcessor; InheritanceInstanceCreator(Class processedType, DeserializationModelCreator deserializationModelCreator, TypeInheritanceConfiguration typeInheritanceConfiguration, ModelDeserializer defaultProcessor) { this.processedType = processedType; this.deserializationModelCreator = deserializationModelCreator; this.typeInheritanceConfiguration = typeInheritanceConfiguration; this.defaultProcessor = defaultProcessor; } @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { String alias; JsonParser jsonParser; String polymorphismKeyName = typeInheritanceConfiguration.getFieldName(); JsonObject object = parser.getObject(); alias = object.getString(polymorphismKeyName, null); JsonObject newJsonObject = context.getJsonbContext().getJsonProvider().createObjectBuilder(object) .remove(polymorphismKeyName) .build(); jsonParser = new JsonStructureToParserAdapter(newJsonObject); //To get to the first event Event event = jsonParser.next(); context.setLastValueEvent(event); Class polymorphicTypeClass; if (alias == null) { return defaultProcessor.deserialize(jsonParser, context); } polymorphicTypeClass = getPolymorphicTypeClass(alias); if (polymorphicTypeClass.equals(processedType)) { return defaultProcessor.deserialize(jsonParser, context); } ModelDeserializer deserializer = deserializationModelCreator.deserializerChain(polymorphicTypeClass); return deserializer.deserialize(jsonParser, context); } @Override public String toString() { return "Property " + typeInheritanceConfiguration.getFieldName() + " polymorphic information handler"; } private Class getPolymorphicTypeClass(String alias) { if (resolvedClasses.containsKey(alias)) { return resolvedClasses.get(alias); } for (Map.Entry, String> entry : typeInheritanceConfiguration.getAliases().entrySet()) { if (entry.getValue().equals(alias)) { resolvedClasses.put(alias, entry.getKey()); return entry.getKey(); } } throw new JsonbException("Unknown alias \"" + alias + "\" of the type " + processedType.getName() + ". Known aliases: " + typeInheritanceConfiguration.getAliases().values()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import static org.eclipse.yasson.internal.deserializer.ObjectDeserializer.NOOP; import static org.eclipse.yasson.internal.deserializer.ObjectDeserializer.VALUE_SKIPPERS; /** * Creator of the Object instance with the usage of the {@link JsonbCreator}. */ class JsonbCreatorDeserializer implements ModelDeserializer { private final Map> propertyDeserializerChains; private final Map> defaultCreatorValues; private final List creatorParams; private final Set ignoredProperties; private final JsonbCreator creator; private final Class clazz; private final Function renamer; private final boolean failOnUnknownProperties; JsonbCreatorDeserializer(Map> propertyDeserializerChains, Map> defaultCreatorValues, JsonbCreator creator, Class clazz, Function renamer, boolean failOnUnknownProperties, Set ignoredProperties) { this.propertyDeserializerChains = propertyDeserializerChains; this.defaultCreatorValues = defaultCreatorValues; this.creatorParams = Arrays.stream(creator.getParams()).map(CreatorModel::getName).collect(Collectors.toList()); this.ignoredProperties = Set.copyOf(ignoredProperties); this.creator = creator; this.clazz = clazz; this.renamer = renamer; this.failOnUnknownProperties = failOnUnknownProperties; } @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { String key = null; Map paramValues = new HashMap<>(); while (parser.hasNext()) { final JsonParser.Event next = parser.next(); context.setLastValueEvent(next); switch (next) { case KEY_NAME: key = renamer.apply(parser.getString()); break; case VALUE_NULL: case START_OBJECT: case START_ARRAY: case VALUE_STRING: case VALUE_NUMBER: case VALUE_FALSE: case VALUE_TRUE: if (propertyDeserializerChains.containsKey(key)) { try { Object o = propertyDeserializerChains.get(key).deserialize(parser, context); if (creatorParams.contains(key)) { paramValues.put(key, o); } } catch (JsonbException e) { throw new JsonbException("Unable to deserialize property '" + key + "' because of: " + e.getMessage(), e); } } else if (failOnUnknownProperties && !ignoredProperties.contains(key)) { throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_JSON_PROPERTY, key, clazz)); } else { //We need to skip the corresponding structure if property key was not found VALUE_SKIPPERS.getOrDefault(next, NOOP).accept(parser); } break; case END_OBJECT: Object[] params = new Object[creatorParams.size()]; for (int i = 0; i < creatorParams.size(); i++) { String param = creatorParams.get(i); if (paramValues.containsKey(param)) { params[i] = paramValues.get(param); } else { params[i] = defaultCreatorValues.get(param).deserialize(null, context); } } context.setInstance(creator.call(params, clazz)); context.getDeferredDeserializers().forEach(Runnable::run); context.getDeferredDeserializers().clear(); return context.getInstance(); default: throw new JsonbException("Unexpected state: " + next); } } return context.getInstance(); } @Override public String toString() { return "ObjectInstanceCreator{" + "parameters=" + creatorParams + ", clazz=" + clazz + '}'; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/JustReturn.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Return passed in object value. */ public final class JustReturn implements ModelDeserializer { private static final JustReturn INSTANCE = new JustReturn(); private JustReturn() { } /** * Return instance. * * @return instance of the class */ public static JustReturn instance() { return INSTANCE; } @Override public Object deserialize(Object value, DeserializationContextImpl context) { return value; } @Override public String toString() { return "No other operations will be performed"; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.Map; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Map container deserializer. */ class MapDeserializer implements ModelDeserializer { private final ModelDeserializer keyDelegate; private final ModelDeserializer valueDelegate; MapDeserializer(ModelDeserializer keyDelegate, ModelDeserializer valueDelegate) { this.keyDelegate = keyDelegate; this.valueDelegate = valueDelegate; } @SuppressWarnings("unchecked") @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { Map map = (Map) context.getInstance(); Object key = null; Object keyValue = null; String keyName = null; Mode mode = Mode.NONE; State state = State.NEXT; while (parser.hasNext()) { final JsonParser.Event next = parser.next(); context.setLastValueEvent(next); switch (next) { case KEY_NAME: mode = mode == Mode.NONE ? Mode.NORMAL : mode; if (mode == Mode.NORMAL) { keyValue = deserializeValue(parser, context, keyDelegate); } keyName = parser.getString(); break; case START_OBJECT: mode = mode == Mode.NONE ? Mode.OBJECT : mode; case START_ARRAY: case VALUE_STRING: case VALUE_TRUE: case VALUE_FALSE: case VALUE_NUMBER: case VALUE_NULL: if (mode == Mode.OBJECT) { if (state == State.NEXT) { state = State.KEY; } else if (state == State.KEY) { validateKeyName(keyName, state); key = deserializeValue(parser, context, keyDelegate); state = State.VALUE; } else if (state == State.VALUE) { validateKeyName(keyName, state); Object value = deserializeValue(parser, context, valueDelegate); map.put(key, value); state = State.DONE; } else { throw new JsonbException("Only attributes 'key' and 'value' allowed!"); } } else { Object value = deserializeValue(parser, context, valueDelegate); map.put(keyValue, value); } break; case END_OBJECT: state = State.NEXT; if (mode == Mode.OBJECT) { break; } case END_ARRAY: return map; default: throw new JsonbException("Unexpected state: " + next); } } return map; } private void validateKeyName(String keyName, State state) { if (state == State.KEY && !keyName.equals("key")) { throw new JsonbException("Attribute name has to be 'key' when representing map entry key. Got: " + keyName); } else if (state == State.VALUE && !keyName.equals("value")) { throw new JsonbException("Attribute name has to be 'value' when representing map entry value. Got: " + keyName); } } private Object deserializeValue(JsonParser parser, DeserializationContextImpl context, ModelDeserializer deserializer) { DeserializationContextImpl keyContext = new DeserializationContextImpl(context); return deserializer.deserialize(parser, keyContext); } private enum Mode { NONE, NORMAL, OBJECT } private enum State { NEXT, VALUE, KEY, DONE } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/MapInstanceCreator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.HashMap; import java.util.Map; import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.InstanceCreator; import org.eclipse.yasson.internal.JsonbConfigProperties; /** * Map instance creator. */ class MapInstanceCreator implements ModelDeserializer { private final MapDeserializer delegate; private final JsonbConfigProperties configProperties; private final Class clazz; MapInstanceCreator(MapDeserializer delegate, JsonbConfigProperties configProperties, Class clazz) { this.delegate = delegate; this.configProperties = configProperties; this.clazz = clazz; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Map map = createInstance(clazz); context.setInstance(map); return delegate.deserialize(value, context); } private Map createInstance(Class clazz) { return clazz.isInterface() ? getMapImpl(clazz) : (Map) InstanceCreator.createInstance(clazz); } private Map getMapImpl(Class ifcType) { if (ConcurrentMap.class.isAssignableFrom(ifcType)) { if (SortedMap.class.isAssignableFrom(ifcType) || NavigableMap.class.isAssignableFrom(ifcType)) { return new ConcurrentSkipListMap<>(); } else { return new ConcurrentHashMap<>(); } } // SortedMap, NavigableMap if (SortedMap.class.isAssignableFrom(ifcType)) { Class defaultMapImplType = configProperties.getDefaultMapImplType(); return SortedMap.class.isAssignableFrom(defaultMapImplType) ? (Map) InstanceCreator.createInstance(defaultMapImplType) : new TreeMap<>(); } return new HashMap<>(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ModelDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Type deserializer. *
* All the instances are required to be reusable and without any states * stored in the class fields. * * @param represents the content value this deserializer is using */ public interface ModelDeserializer { /** * Deserialize provided value or delegate deserialization to the next deserializer. * * @param value value to be deserialized * @param context deserialization context * @return deserialized value */ Object deserialize(T value, DeserializationContextImpl context); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/NullCheckDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Json null value checker. *
* Simple delegate which checks whether the obtained parser value event was * {@link JsonParser.Event#VALUE_NULL} or not. If the event has been {@link JsonParser.Event#VALUE_NULL}, null value * deserializer will be called. In all other cases non-null deserializer is called. */ public class NullCheckDeserializer implements ModelDeserializer { private final ModelDeserializer nonNullDeserializer; private final ModelDeserializer nullDeserializer; /** * Create new instance. * * @param nonNullDeserializer deserializer called when value is not null * @param nullDeserializer deserializer called when value is null */ public NullCheckDeserializer(ModelDeserializer nonNullDeserializer, ModelDeserializer nullDeserializer) { this.nonNullDeserializer = nonNullDeserializer; this.nullDeserializer = nullDeserializer; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (context.getLastValueEvent() != JsonParser.Event.VALUE_NULL) { return nonNullDeserializer.deserialize(value, context); } return nullDeserializer.deserialize(null, context); } @Override public String toString() { return "Null value check"; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java ================================================ /* * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.EnumMap; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Object container deserializer. */ class ObjectDeserializer implements ModelDeserializer { static final Consumer NOOP = jsonParser -> {}; static final EnumMap> VALUE_SKIPPERS = new EnumMap<>(JsonParser.Event.class); static { VALUE_SKIPPERS.put(JsonParser.Event.START_OBJECT, JsonParser::skipObject); VALUE_SKIPPERS.put(JsonParser.Event.START_ARRAY, JsonParser::skipArray); } private final Map> propertyDeserializerChains; private final Function renamer; private final Class rawClass; private final boolean failOnUnknownProperty; private final Set ignoredProperties; ObjectDeserializer(Map> propertyDeserializerChains, Function renamer, Class rawClass, boolean failOnUnknownProperty, Set ignoredProperties) { this.propertyDeserializerChains = Map.copyOf(propertyDeserializerChains); this.renamer = renamer; this.rawClass = rawClass; this.failOnUnknownProperty = failOnUnknownProperty; this.ignoredProperties = Set.copyOf(ignoredProperties); } @Override public Object deserialize(JsonParser parser, DeserializationContextImpl context) { String key = null; while (parser.hasNext()) { final JsonParser.Event next = parser.next(); context.setLastValueEvent(next); switch (next) { case KEY_NAME: key = renamer.apply(parser.getString()); break; case VALUE_NULL: case START_OBJECT: case START_ARRAY: case VALUE_STRING: case VALUE_NUMBER: case VALUE_FALSE: case VALUE_TRUE: if (propertyDeserializerChains.containsKey(key)) { try { propertyDeserializerChains.get(key).deserialize(parser, context); } catch (JsonbException e) { throw new JsonbException("Unable to deserialize property '" + key + "' because of: " + e.getMessage(), e); } } else if (failOnUnknownProperty && !ignoredProperties.contains(key)) { throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_JSON_PROPERTY, key, rawClass)); } else { //We need to skip the corresponding structure if property key was not found VALUE_SKIPPERS.getOrDefault(next, NOOP).accept(parser); } break; case END_ARRAY: break; case END_OBJECT: return context.getInstance(); default: throw new JsonbException("Unexpected state: " + next); } } return context.getInstance(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/OptionalDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.util.Optional; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Optional} types. */ class OptionalDeserializer implements ModelDeserializer { private final ModelDeserializer typeDeserializer; private final ModelDeserializer delegate; OptionalDeserializer(ModelDeserializer typeDeserializer, ModelDeserializer delegate) { this.typeDeserializer = typeDeserializer; this.delegate = delegate; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Optional val = Optional.ofNullable(typeDeserializer.deserialize(value, context)); return delegate.deserialize(val, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/PositionChecker.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Map; import java.util.Set; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import static jakarta.json.stream.JsonParser.Event; /** * JSON document position checker. *
* Checks whether json parser is in expected state. If not it will try to skip to the next event, since * if user defined components are involved, it is possible to expect incorrect states in terms of the last expected events. * If this checker is still not in expected state, an exception is thrown. */ public class PositionChecker implements ModelDeserializer { private static final Map CLOSING_EVENTS = Map.of(Event.START_ARRAY, Event.END_ARRAY, Event.START_OBJECT, Event.END_OBJECT); private final Set expectedEvents; private final ModelDeserializer delegate; private final Type rType; /** * Create new instance. * * @param delegate delegate which is call after the check * @param rType runtime type * @param checker bound group of events */ public PositionChecker(ModelDeserializer delegate, Type rType, Checker checker) { this(checker.events, delegate, rType); } /** * Create new instance. * * @param delegate delegate which is call after the check * @param rType runtime type * @param events customized checked events */ public PositionChecker(ModelDeserializer delegate, Type rType, Event... events) { this(Set.copyOf(Arrays.asList(events)), delegate, rType); } private PositionChecker(Set expectedEvents, ModelDeserializer delegate, Type rType) { this.expectedEvents = expectedEvents; this.delegate = delegate; this.rType = rType; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Event original = context.getLastValueEvent(); Event startEvent = original; if (!expectedEvents.contains(startEvent)) { startEvent = value.next(); context.setLastValueEvent(startEvent); if (!expectedEvents.contains(startEvent)) { throw new JsonbException("Incorrect position for processing type: " + rType + ". " + "Received event: " + original + " " + "Allowed: " + expectedEvents); } } Object o = delegate.deserialize(value, context); if (CLOSING_EVENTS.containsKey(startEvent) && CLOSING_EVENTS.get(startEvent) != context.getLastValueEvent()) { throw new JsonbException("Incorrect parser position after processing of the type: " + rType + ". " + "Start event: " + startEvent + " " + "After processing event: " + context.getLastValueEvent()); } return o; } @Override public String toString() { return "PositionChecker{" + "expectedEvents=" + expectedEvents + ", runtimeType=" + rType + '}'; } /** * Grouped events according to whether it is container or value. */ public enum Checker { /** * Value bound events. */ VALUES(Event.VALUE_FALSE, Event.VALUE_TRUE, Event.VALUE_STRING, Event.VALUE_NUMBER, Event.VALUE_NULL), /** * Container bound events. */ CONTAINER(Event.START_OBJECT, Event.START_ARRAY); private final Set events; Checker(Event... events) { this.events = Set.of(events); } /** * Return events bound to the event group. * * @return set of bound events */ public Set getEvents() { return events; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/RequiredCreatorParameter.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; class RequiredCreatorParameter implements ModelDeserializer { private final String parameterName; RequiredCreatorParameter(String parameterName) { this.parameterName = parameterName; } @Override public Object deserialize(Object value, DeserializationContextImpl context) { throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CREATOR_MISSING_PROPERTY, parameterName)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.reflect.Type; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.model.customization.Customization; /** * Deserializer used to invoke user defined deserializers. */ class UserDefinedDeserializer implements ModelDeserializer { private final JsonbDeserializer userDefinedDeserializer; private final ModelDeserializer delegate; private final Type rType; private final Customization customization; //TODO remove or not? deserializer cycle // public UserDefinedDeserializer(JsonbDeserializer userDefinedDeserializer, // ModelDeserializer exactType, // ModelDeserializer delegate, // Type rType, // Customization customization) { // this.userDefinedDeserializer = userDefinedDeserializer; // this.exactType = exactType; // this.delegate = delegate; // this.rType = rType; // this.customization = customization; // } UserDefinedDeserializer(JsonbDeserializer userDefinedDeserializer, ModelDeserializer delegate, Type rType, Customization customization) { this.userDefinedDeserializer = userDefinedDeserializer; this.delegate = delegate; this.rType = rType; this.customization = customization; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { DeserializationContextImpl newContext = new DeserializationContextImpl(context); newContext.setCustomization(customization); //TODO remove or not? deserializer cycle // if (context.getUserProcessorChain().contains(userDefinedDeserializer.getClass())) { // if (context.getLastValueEvent() != JsonParser.Event.START_ARRAY // && context.getLastValueEvent() != JsonParser.Event.START_OBJECT) { // newContext.setDisableNextPositionCheck(true); // } // return exactType.deserialize(value, newContext); // } // newContext.getUserProcessorChain().add(userDefinedDeserializer.getClass()); YassonParser yassonParser = new YassonParser(value, context.getLastValueEvent(), newContext); Object object = userDefinedDeserializer.deserialize(yassonParser, newContext, rType); yassonParser.skipRemaining(); context.setLastValueEvent(newContext.getLastValueEvent()); return delegate.deserialize(object, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ValueExtractor.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.types.TypeDeserializer; /** * Extracts the value out of the {@link JsonParser} based upon the last obtained event. */ public class ValueExtractor implements ModelDeserializer { private final TypeDeserializer delegate; /** * Create new instance. * * @param delegate delegate to accept extracted value */ public ValueExtractor(TypeDeserializer delegate) { this.delegate = delegate; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { JsonParser.Event last = context.getLastValueEvent(); switch (last) { case VALUE_TRUE: return delegate.deserialize(Boolean.TRUE, context); case VALUE_FALSE: return delegate.deserialize(Boolean.FALSE, context); case KEY_NAME: case VALUE_STRING: return delegate.deserialize(value.getString(), context); case VALUE_NUMBER: //We don't know for sure how to handle the number value, it can be int, long etc. //Value extraction has to be delegated to the TypeDeserializer return delegate.deserialize(value, context); default: throw new JsonbException("Could not extract data. Received event: " + last); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/ValueSetterDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.lang.invoke.MethodHandle; import java.util.Objects; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Value setter. Invokes created {@link MethodHandle} to set deserialized value to the instance. */ class ValueSetterDeserializer implements ModelDeserializer { private final MethodHandle valueSetter; ValueSetterDeserializer(MethodHandle valueSetter) { this.valueSetter = Objects.requireNonNull(valueSetter); } @Override public Object deserialize(Object value, DeserializationContextImpl context) { Object object = context.getInstance(); try { valueSetter.invoke(object, value); return value; } catch (Throwable e) { throw new JsonbException("Error setting value on: " + object, e); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2026 Contributors to the Eclipse Foundation. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer; import java.math.BigDecimal; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Stream; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Yasson {@link YassonParser} parser wrapper. *
* Used for user defined deserializers. Does not allow deserializer to read outside the scope it should be used on. */ class YassonParser implements JsonParser { private final JsonParser delegate; private final DeserializationContextImpl context; private int level; YassonParser(JsonParser delegate, Event firstEvent, DeserializationContextImpl context) { this.delegate = delegate; this.context = context; this.level = determineLevelValue(firstEvent); } private int determineLevelValue(Event firstEvent) { switch (firstEvent) { case START_ARRAY: case START_OBJECT: return 1; //container start, there will be more events to come default: return 0; //just this single value, do not allow reading more } } void skipRemaining() { while (hasNext()) { next(); } } @Override public boolean hasNext() { if (level < 1) { return false; } return delegate.hasNext(); } @Override public Event next() { validate(); Event next = delegate.next(); context.setLastValueEvent(next); switch (next) { case START_OBJECT: case START_ARRAY: level++; break; case END_OBJECT: case END_ARRAY: level--; break; default: //no other changes needed } return next; } @Override public Event currentEvent() { return context.getLastValueEvent(); } @Override public String getString() { return delegate.getString(); } @Override public boolean isIntegralNumber() { return delegate.isIntegralNumber(); } @Override public int getInt() { return delegate.getInt(); } @Override public long getLong() { return delegate.getLong(); } @Override public BigDecimal getBigDecimal() { return delegate.getBigDecimal(); } @Override public JsonLocation getLocation() { return delegate.getLocation(); } @Override public JsonObject getObject() { validate(); level--; JsonObject jsonObject = delegate.getObject(); context.setLastValueEvent(Event.END_OBJECT); return jsonObject; } @Override public JsonValue getValue() { final Event currentLevel = context.getLastValueEvent(); switch (currentLevel) { case START_ARRAY: return getArray(); case START_OBJECT: return getObject(); default: return delegate.getValue(); } } @Override public JsonArray getArray() { validate(); level--; JsonArray array = delegate.getArray(); context.setLastValueEvent(Event.END_ARRAY); return array; } @Override public Stream getArrayStream() { validate(); level--; return delegate.getArrayStream(); } @Override public Stream> getObjectStream() { validate(); level--; return delegate.getObjectStream(); } @Override public Stream getValueStream() { validate(); level--; return delegate.getValueStream(); } @Override public void skipArray() { validate(); level--; delegate.skipArray(); } @Override public void skipObject() { validate(); level--; delegate.skipObject(); } @Override public void close() { throw new UnsupportedOperationException(); } private void validate() { if (level < 1) { throw new NoSuchElementException("There are no more elements available!"); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractDateDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.sql.Date; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.Optional; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbDateFormat; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.deserializer.JustReturn; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Base deserializer for all the date related types. */ abstract class AbstractDateDeserializer extends TypeDeserializer { static final ZoneId UTC = ZoneId.of("UTC"); private ModelDeserializer actualDeserializer; AbstractDateDeserializer(TypeDeserializerBuilder builder) { super(builder); this.actualDeserializer = actualDeserializer(builder.getConfigProperties(), builder.getCustomization()); } AbstractDateDeserializer(Class clazz) { super(new TypeDeserializerBuilder(clazz, null, null, JustReturn.instance())); this.actualDeserializer = null; } private ModelDeserializer actualDeserializer(JsonbConfigProperties properties, Customization customization) { final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization); if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat())) { return (value, context) -> fromInstant(Instant.ofEpochMilli(Long.parseLong(value))); } else if (formatter.getDateTimeFormatter() != null) { return (value, context) -> parseWithFormatterInternal(value, formatter.getDateTimeFormatter()); } else { DateTimeFormatter configDateTimeFormatter = properties.getConfigDateFormatter().getDateTimeFormatter(); if (configDateTimeFormatter != null) { return (value, context) -> parseWithFormatterInternal(value, configDateTimeFormatter); } } if (properties.isStrictIJson()) { return (value, context) -> parseWithFormatterInternal(value, JsonbDateFormatter.IJSON_DATE_FORMATTER); } Locale locale = properties.getLocale(formatter.getLocale()); return (value, context) -> { try { return parseDefault(value, locale); } catch (DateTimeException e) { throw new JsonbException(Messages.getMessage(MessageKeys.DATE_PARSE_ERROR, value, getType()), e); } }; } private JsonbDateFormatter getJsonbDateFormatter(JsonbConfigProperties properties, Customization customization) { return Optional.ofNullable(customization.getDeserializeDateFormatter()) .orElse(properties.getConfigDateFormatter()); } @Override public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { if (actualDeserializer == null) { actualDeserializer = actualDeserializer(context.getJsonbContext().getConfigProperties(), context.getCustomization()); } return actualDeserializer.deserialize(value, context); } /** * Construct date object from an instant containing epoch millisecond. * If date object supports zone offset / zone id, system default is used and warning is logged. * * @param instant instant to construct from * @return date object */ abstract T fromInstant(Instant instant); /** * Parse java.time date object with default formatter. * Different default formatter for each date object type is used. * * @param jsonValue string value to parse from * @param locale annotated locale or default * @return parsed date object */ abstract T parseDefault(String jsonValue, Locale locale); /** * Parse java.time date object with provided formatter. * * @param jsonValue string value to parse from * @param formatter a formatter to use * @return parsed date object */ abstract T parseWithFormatter(String jsonValue, DateTimeFormatter formatter); private T parseWithFormatterInternal(String jsonValue, DateTimeFormatter formatter) { try { return parseWithFormatter(jsonValue, formatter); } catch (DateTimeException e) { throw new JsonbException(Messages.getMessage(MessageKeys.DATE_PARSE_ERROR, jsonValue, getType()), e); } } protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) { return formatter.getZone() != null ? formatter : formatter.withZone(UTC); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import java.util.function.Function; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Base deserializer for all the number types. */ abstract class AbstractNumberDeserializer extends TypeDeserializer { private final ModelDeserializer actualDeserializer; private final boolean integerOnly; AbstractNumberDeserializer(TypeDeserializerBuilder builder, boolean integerOnly) { super(builder); this.actualDeserializer = actualDeserializer(builder); this.integerOnly = integerOnly; } private ModelDeserializer actualDeserializer(TypeDeserializerBuilder builder) { Customization customization = builder.getCustomization(); if (customization.getDeserializeNumberFormatter() == null) { return (value, context) -> { try { return parseNumberValue(value); } catch (NumberFormatException e) { throw new JsonbException(Messages.getMessage(MessageKeys.DESERIALIZE_VALUE_ERROR, getType()), e); } }; } final JsonbNumberFormatter numberFormat = customization.getDeserializeNumberFormatter(); //consider synchronizing on format instance or per thread cache. Locale locale = builder.getConfigProperties().getLocale(numberFormat.getLocale()); final NumberFormat format = NumberFormat.getInstance(locale); ((DecimalFormat) format).applyPattern(numberFormat.getFormat()); format.setParseIntegerOnly(integerOnly); Function valueChanger = createCompatibilityValueChanger(locale); return (value, context) -> { try { String updated = valueChanger.apply(value); return parseNumberValue(String.valueOf(format.parse(updated))); } catch (ParseException e) { throw new JsonbException(Messages.getMessage(MessageKeys.PARSING_NUMBER, value, numberFormat.getFormat()), e); } }; } private Function createCompatibilityValueChanger(Locale locale) { char beforeJdk13GroupSeparator = '\u00A0'; char frenchGroupingSeparator = DecimalFormatSymbols.getInstance(Locale.FRENCH).getGroupingSeparator(); if (locale.getLanguage().equals(Locale.FRENCH.getLanguage()) && beforeJdk13GroupSeparator != frenchGroupingSeparator) { //JDK-8225245 return value -> value.replace(beforeJdk13GroupSeparator, frenchGroupingSeparator); } return value -> value; } abstract T parseNumberValue(String value); @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return actualDeserializer.deserialize(value, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/BigDecimalDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.math.BigDecimal; /** * Deserializer of the {@link BigDecimal} type. */ class BigDecimalDeserializer extends AbstractNumberDeserializer { BigDecimalDeserializer(TypeDeserializerBuilder builder) { super(builder, false); } @Override BigDecimal parseNumberValue(String value) { return new BigDecimal(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/BigIntegerDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.math.BigInteger; /** * Deserializer of the {@link BigInteger} type. */ class BigIntegerDeserializer extends AbstractNumberDeserializer { BigIntegerDeserializer(TypeDeserializerBuilder builder) { super(builder, true); } @Override BigInteger parseNumberValue(String value) { return new BigInteger(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/BooleanDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Boolean} type. */ class BooleanDeserializer extends TypeDeserializer { BooleanDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return Boolean.parseBoolean(value); } @Override Object deserializeBooleanValue(boolean value, DeserializationContextImpl context, Type rType) { return value; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ByteDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; /** * Deserializer of the {@link Byte} type. */ class ByteDeserializer extends AbstractNumberDeserializer { ByteDeserializer(TypeDeserializerBuilder builder) { super(builder, true); } @Override Byte parseNumberValue(String value) { return Byte.parseByte(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/CalendarDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; /** * Deserializer of the {@link Calendar} type. */ class CalendarDeserializer extends AbstractDateDeserializer { private static final LocalTime ZERO_LOCAL_TIME = LocalTime.parse("00:00:00"); private final Calendar calendarTemplate; CalendarDeserializer(TypeDeserializerBuilder builder) { super(builder); this.calendarTemplate = new GregorianCalendar(); this.calendarTemplate.clear(); this.calendarTemplate.setTimeZone(TimeZone.getTimeZone(UTC)); } @Override Calendar fromInstant(Instant instant) { final Calendar calendar = (Calendar) calendarTemplate.clone(); calendar.setTimeInMillis(instant.toEpochMilli()); return calendar; } @Override Calendar parseDefault(String jsonValue, Locale locale) { DateTimeFormatter formatter = jsonValue.contains("T") ? DateTimeFormatter.ISO_DATE_TIME : DateTimeFormatter.ISO_DATE; return parseWithFormatter(jsonValue, formatter.withLocale(locale)); } @Override Calendar parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { final TemporalAccessor parsed = formatter.parse(jsonValue); LocalTime time = parsed.query(TemporalQueries.localTime()); ZoneId zone = parsed.query(TemporalQueries.zone()); if (zone == null) { zone = UTC; } if (time == null) { time = ZERO_LOCAL_TIME; } ZonedDateTime result = LocalDate.from(parsed).atTime(time).atZone(zone); return GregorianCalendar.from(result); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/CharDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Character} type. */ class CharDeserializer extends TypeDeserializer { CharDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return value.charAt(0); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/DateDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.Locale; /** * Deserializer of the {@link Date} type. * *

* For date-only patterns (e.g., "yyyy-MM-dd"), this deserializer uses {@link DateTimeFormatter#parseBest} to detect the * appropriate temporal type [ZonedDateTime, LocalDateTime, LocalDate, or YearMonth] * and creates the Date object at midnight in the specified * timezone. When no timezone is specified in the pattern, UTC is used as required by Jakarta JSON Binding specification * section 3.5. *

*

* critical, use {@link java.time.LocalDate} (recommended) or {@link java.sql.Date}. *

*/ class DateDeserializer extends AbstractDateDeserializer { private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_DATE_TIME; DateDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Date fromInstant(Instant instant) { return new Date(instant.toEpochMilli()); } @Override Date parseDefault(String jsonValue, Locale locale) { return parseWithOrWithoutZone(jsonValue, DEFAULT_DATE_TIME_FORMATTER.withLocale(locale)); } @Override Date parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return parseWithOrWithoutZone(jsonValue, formatter); } private static Date parseWithOrWithoutZone(String jsonValue, DateTimeFormatter formatter) { final TemporalAccessor best = formatter.parseBest(jsonValue, ZonedDateTime::from, LocalDateTime::from, LocalDate::from, YearMonth::from); // If no zone provided in string, use the formatter's zone or UTC per the Jakarta JSON Binding specification // section 3.5 final ZoneId zone = formatter.getZone() != null ? formatter.getZone() : ZoneOffset.UTC; // Determine the type of the best option final Instant instant; if (best instanceof ZonedDateTime) { instant = ((ZonedDateTime) best).toInstant(); } else if (best instanceof LocalDateTime) { instant = ((LocalDateTime) best).atZone(zone).toInstant(); } else if (best instanceof LocalDate) { instant = LocalDate.from(best).atStartOfDay(zone).toInstant(); } else if (best instanceof YearMonth) { instant = ((YearMonth) best).atDay(1).atStartOfDay(zone).toInstant(); } else { // Fallback instant = Instant.from(best); } return Date.from(instant); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/DoubleDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; /** * Deserializer of the {@link Double} type. */ class DoubleDeserializer extends AbstractNumberDeserializer { DoubleDeserializer(TypeDeserializerBuilder builder) { super(builder, false); } @Override Double parseNumberValue(String value) { return Double.parseDouble(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/DurationDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.time.Duration; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Duration} type. */ class DurationDeserializer extends TypeDeserializer { DurationDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return Duration.parse(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Enum}. */ class EnumDeserializer extends TypeDeserializer { EnumDeserializer(TypeDeserializerBuilder builder) { super(builder); } @SuppressWarnings("unchecked") @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return Enum.valueOf((Class) rType, value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/FloatDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; /** * Deserializer of the {@link Float} type. */ class FloatDeserializer extends AbstractNumberDeserializer { FloatDeserializer(TypeDeserializerBuilder builder) { super(builder, false); } @Override Float parseNumberValue(String value) { return Float.parseFloat(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/InstantDeserializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Deserializer of the {@link Instant} type. */ class InstantDeserializer extends AbstractDateDeserializer { private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_INSTANT.withZone(UTC); InstantDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected Instant fromInstant(Instant instant) { return instant; } @Override protected Instant parseDefault(String jsonValue, Locale locale) { return Instant.from(DEFAULT_FORMATTER.withLocale(locale).parse(jsonValue)); } @Override protected Instant parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return Instant.from(getZonedFormatter(formatter).parse(jsonValue)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/IntegerDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Integer} type. */ class IntegerDeserializer extends AbstractNumberDeserializer { IntegerDeserializer(TypeDeserializerBuilder builder) { super(builder, true); } @Override Integer parseNumberValue(String value) { return Integer.parseInt(value); } @Override Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) { return value.getInt(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/JsonValueDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link JsonValue} type. */ class JsonValueDeserializer implements ModelDeserializer { private final ModelDeserializer delegate; private final JsonValue nullValue; JsonValueDeserializer(TypeDeserializerBuilder builder, JsonValue nullValue) { this.delegate = builder.getDelegate(); this.nullValue = nullValue; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { JsonParser.Event last = context.getLastValueEvent(); return delegate.deserialize(deserializeValue(last, value), context); } private JsonValue deserializeValue(JsonParser.Event last, JsonParser parser) { switch (last) { case VALUE_TRUE: return JsonValue.TRUE; case VALUE_FALSE: return JsonValue.FALSE; case VALUE_NULL: return nullValue; case VALUE_STRING: case VALUE_NUMBER: return parser.getValue(); case START_OBJECT: return parser.getObject(); case START_ARRAY: return parser.getArray(); default: throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Unknown JSON value: " + last)); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Deserializer of the {@link LocalDate} type. */ class LocalDateDeserializer extends AbstractDateDeserializer { LocalDateDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected LocalDate fromInstant(Instant instant) { return instant.atZone(UTC).toLocalDate(); } @Override protected LocalDate parseDefault(String jsonValue, Locale locale) { return LocalDate.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_DATE.withLocale(locale)); } @Override protected LocalDate parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return LocalDate.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateTimeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Deserializer of the {@link LocalDateTime} type. */ class LocalDateTimeDeserializer extends AbstractDateDeserializer { LocalDateTimeDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected LocalDateTime fromInstant(Instant instant) { return LocalDateTime.ofInstant(instant, UTC); } @Override protected LocalDateTime parseDefault(String jsonValue, Locale locale) { return LocalDateTime.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_DATE_TIME.withLocale(locale)); } @Override protected LocalDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return LocalDateTime.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalTimeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link LocalTime} type. */ class LocalTimeDeserializer extends AbstractDateDeserializer { LocalTimeDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected LocalTime fromInstant(Instant instant) { throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, LocalTime.class.getSimpleName())); } @Override protected LocalTime parseDefault(String jsonValue, Locale locale) { return LocalTime.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_TIME.withLocale(locale)); } @Override protected LocalTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return LocalTime.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/LongDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Long} type. */ class LongDeserializer extends AbstractNumberDeserializer { LongDeserializer(TypeDeserializerBuilder builder) { super(builder, true); } @Override Long parseNumberValue(String value) { return Long.parseLong(value); } @Override Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) { return value.getLong(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/MonthDayTypeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Deserializer of the {@link MonthDay} type. */ class MonthDayTypeDeserializer extends AbstractDateDeserializer { private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("--MM-dd").withZone(UTC); MonthDayTypeDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected MonthDay fromInstant(Instant instant) { return MonthDay.from(instant.atZone(UTC)); } @Override protected MonthDay parseDefault(String jsonValue, Locale locale) { return MonthDay.parse(jsonValue, DEFAULT_FORMAT.withLocale(locale)); } @Override protected MonthDay parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return MonthDay.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/NumberDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.math.BigDecimal; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Number} type. */ class NumberDeserializer extends TypeDeserializer { NumberDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return new BigDecimal(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ObjectTypeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.List; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; /** * Deserializer of the {@link Object} type. */ class ObjectTypeDeserializer implements ModelDeserializer { private static final Type LIST = List.class; private final ModelDeserializer delegate; private final Class mapClass; ObjectTypeDeserializer(TypeDeserializerBuilder builder) { this.delegate = builder.getDelegate(); this.mapClass = builder.getConfigProperties().getDefaultMapImplType(); } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { Object toSet; switch (context.getLastValueEvent()) { case VALUE_TRUE: toSet = Boolean.TRUE; break; case VALUE_FALSE: toSet = Boolean.FALSE; break; case VALUE_NUMBER: toSet = new BigDecimal(value.getString()); break; case KEY_NAME: case VALUE_STRING: toSet = value.getString(); break; case START_OBJECT: DeserializationContextImpl newContext = new DeserializationContextImpl(context); toSet = newContext.deserialize(mapClass, value); break; case START_ARRAY: DeserializationContextImpl newContext1 = new DeserializationContextImpl(context); toSet = newContext1.deserialize(LIST, value); break; default: throw new JsonbException("Unexpected event: " + context.getLastValueEvent()); } return delegate.deserialize(toSet, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetDateTimeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.logging.Logger; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link OffsetDateTime} type. */ class OffsetDateTimeDeserializer extends AbstractDateDeserializer { private static final Logger LOGGER = Logger.getLogger(OffsetDateTimeDeserializer.class.getName()); OffsetDateTimeDeserializer(TypeDeserializerBuilder builder) { super(builder); } /** * fromInstant is called only in case {@link jakarta.json.bind.annotation.JsonbDateFormat} is TIME_IN_MILLIS, * which doesn't make much sense for usage with OffsetDateTime. */ @Override protected OffsetDateTime fromInstant(Instant instant) { LOGGER.warning(Messages.getMessage(MessageKeys.OFFSET_DATE_TIME_FROM_MILLIS, OffsetDateTime.class.getSimpleName(), UTC)); return OffsetDateTime.ofInstant(instant, UTC); } @Override protected OffsetDateTime parseDefault(String jsonValue, Locale locale) { return OffsetDateTime.parse(jsonValue, DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(locale)); } @Override protected OffsetDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return OffsetDateTime.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetTimeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.OffsetTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link OffsetTime} type. */ class OffsetTimeDeserializer extends AbstractDateDeserializer { OffsetTimeDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected OffsetTime fromInstant(Instant instant) { throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, OffsetTime.class.getSimpleName())); } @Override protected OffsetTime parseDefault(String jsonValue, Locale locale) { return OffsetTime.parse(jsonValue, DateTimeFormatter.ISO_OFFSET_TIME.withLocale(locale)); } @Override protected OffsetTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return OffsetTime.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalDoubleDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.util.OptionalDouble; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; /** * Deserializer of the {@link OptionalDouble} type. */ class OptionalDoubleDeserializer implements ModelDeserializer { private final ModelDeserializer extractor; private final ModelDeserializer nullValueDelegate; OptionalDoubleDeserializer(ModelDeserializer extractor, ModelDeserializer nullValueDelegate) { this.extractor = extractor; this.nullValueDelegate = nullValueDelegate; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) { return nullValueDelegate.deserialize(OptionalDouble.empty(), context); } OptionalDouble optional = OptionalDouble.of((Double) extractor.deserialize(value, context)); return nullValueDelegate.deserialize(optional, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalIntDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.util.OptionalInt; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; /** * Deserializer of the {@link OptionalInt} type. */ class OptionalIntDeserializer implements ModelDeserializer { private final ModelDeserializer extractor; private final ModelDeserializer delegate; OptionalIntDeserializer(ModelDeserializer extractor, ModelDeserializer delegate) { this.extractor = extractor; this.delegate = delegate; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) { return delegate.deserialize(OptionalInt.empty(), context); } OptionalInt optional = OptionalInt.of((Integer) extractor.deserialize(value, context)); return delegate.deserialize(optional, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalLongDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.util.OptionalLong; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; /** * Deserializer of the {@link OptionalLong} type. */ class OptionalLongDeserializer implements ModelDeserializer { private final ModelDeserializer extractor; private final ModelDeserializer nullValueDelegate; OptionalLongDeserializer(ModelDeserializer extractor, ModelDeserializer nullValueDelegate) { this.extractor = extractor; this.nullValueDelegate = nullValueDelegate; } @Override public Object deserialize(JsonParser value, DeserializationContextImpl context) { if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) { return nullValueDelegate.deserialize(OptionalLong.empty(), context); } OptionalLong optional = OptionalLong.of((Long) extractor.deserialize(value, context)); return nullValueDelegate.deserialize(optional, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/PathDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.nio.file.Paths; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link java.nio.file.Path} type. */ class PathDeserializer extends TypeDeserializer { PathDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return Paths.get(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/PeriodDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.time.Period; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Period} type. */ class PeriodDeserializer extends TypeDeserializer { PeriodDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return Period.parse(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ShortDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; /** * Deserializer of the {@link Short} type. */ class ShortDeserializer extends AbstractNumberDeserializer { ShortDeserializer(TypeDeserializerBuilder builder) { super(builder, true); } @Override Short parseNumberValue(String value) { return Short.parseShort(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlDateDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.sql.Date; import java.time.Instant; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Locale; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link Date} type. */ public class SqlDateDeserializer extends AbstractDateDeserializer implements JsonbDeserializer { private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE.withZone(UTC); SqlDateDeserializer(TypeDeserializerBuilder builder) { super(builder); } /** * Create new instance. */ public SqlDateDeserializer() { super(Date.class); } @Override protected Date fromInstant(Instant instant) { return new Date(instant.toEpochMilli()); } @Override protected Date parseDefault(String jsonValue, Locale locale) { return Date.valueOf(LocalDate.parse(jsonValue, DEFAULT_FORMATTER.withLocale(locale))); } @Override protected Date parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return Date.valueOf(LocalDate.parse(jsonValue, formatter)); } @Override public Date deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { DeserializationContextImpl context = (DeserializationContextImpl) ctx; return (Date) deserialize(parser.getString(), context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlTimestampDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Locale; /** * Deserializer of the {@link Timestamp} type. */ class SqlTimestampDeserializer extends AbstractDateDeserializer { private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC); SqlTimestampDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected Timestamp fromInstant(Instant instant) { return Timestamp.from(instant); } @Override protected Timestamp parseDefault(String jsonValue, Locale locale) { final TemporalAccessor parsed = DEFAULT_FORMATTER.withLocale(locale).parse(jsonValue); return Timestamp.from(getInstant(parsed)); } @Override protected Timestamp parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { final TemporalAccessor parsed = getZonedFormatter(formatter).parse(jsonValue); return Timestamp.from(getInstant(parsed)); } private Instant getInstant(TemporalAccessor parsed) { LocalDateTime local = LocalDateTime.from(parsed); return local.atZone(ZoneId.of("UTC")).toInstant(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/StringDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link String} type. */ class StringDeserializer extends TypeDeserializer { StringDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { JsonbConfigProperties config = context.getJsonbContext().getConfigProperties(); return checkIJson(value, config); } private String checkIJson(String value, JsonbConfigProperties config) { if (config.isStrictIJson()) { String newString = new String(value.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); if (!newString.equals(value)) { throw new JsonbException(Messages.getMessage(MessageKeys.UNPAIRED_SURROGATE)); } } return value; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/TimeZoneDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.zone.ZoneRulesException; import java.util.SimpleTimeZone; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link java.util.TimeZone} type. */ class TimeZoneDeserializer extends TypeDeserializer { TimeZoneDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { try { final ZoneId zoneId = ZoneId.of(value); final ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(zoneId); return new SimpleTimeZone(zonedDateTime.getOffset().getTotalSeconds() * 1000, zoneId.getId()); } catch (ZoneRulesException e) { throw new JsonbException(Messages.getMessage(MessageKeys.ZONE_PARSE_ERROR, value), e); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.DeserializationContextImpl; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; /** * Base for all type deserializers. */ public abstract class TypeDeserializer implements ModelDeserializer { private final ModelDeserializer delegate; private final Class clazz; TypeDeserializer(TypeDeserializerBuilder builder) { this.delegate = builder.getDelegate(); this.clazz = builder.getClazz(); } @Override public final Object deserialize(String value, DeserializationContextImpl context) { return delegate.deserialize(deserializeStringValue(value, context, clazz), context); } public final Object deserialize(boolean value, DeserializationContextImpl context) { return delegate.deserialize(deserializeBooleanValue(value, context, clazz), context); } public final Object deserialize(JsonParser value, DeserializationContextImpl context) { return delegate.deserialize(deserializeNumberValue(value, context, clazz), context); } abstract Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType); Object deserializeBooleanValue(boolean value, DeserializationContextImpl context, Type rType) { return deserializeStringValue(String.valueOf(value), context, rType); } Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) { return deserializeStringValue(value.getString(), context, rType); } Class getType() { return clazz; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.util.Objects; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.Customization; class TypeDeserializerBuilder { private final Class clazz; private final Customization customization; private final JsonbConfigProperties configProperties; private final ModelDeserializer delegate; TypeDeserializerBuilder(Class clazz, Customization customization, JsonbConfigProperties configProperties, ModelDeserializer delegate) { this.clazz = Objects.requireNonNull(clazz); this.customization = customization == null ? ClassCustomization.empty() : customization; this.configProperties = configProperties; this.delegate = Objects.requireNonNull(delegate); } public Class getClazz() { return clazz; } public JsonbConfigProperties getConfigProperties() { return configProperties; } public ModelDeserializer getDelegate() { return delegate; } public Customization getCustomization() { return customization; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; import java.util.SimpleTimeZone; import java.util.TimeZone; import java.util.UUID; import java.util.function.Function; import javax.xml.datatype.XMLGregorianCalendar; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.deserializer.JustReturn; import org.eclipse.yasson.internal.deserializer.ModelDeserializer; import org.eclipse.yasson.internal.deserializer.NullCheckDeserializer; import org.eclipse.yasson.internal.deserializer.PositionChecker; import org.eclipse.yasson.internal.deserializer.ValueExtractor; import org.eclipse.yasson.internal.model.customization.Customization; import static org.eclipse.yasson.internal.BuiltInTypes.isClassAvailable; /** * Specific type deserializers. */ public class TypeDeserializers { private static final Map, Function> DESERIALIZERS = new HashMap<>(); private static final Map, Class> OPTIONAL_TYPES = new HashMap<>(); static { DESERIALIZERS.put(BigInteger.class, BigIntegerDeserializer::new); DESERIALIZERS.put(BigDecimal.class, BigDecimalDeserializer::new); DESERIALIZERS.put(Boolean.class, BooleanDeserializer::new); DESERIALIZERS.put(Boolean.TYPE, BooleanDeserializer::new); DESERIALIZERS.put(Byte.class, ByteDeserializer::new); DESERIALIZERS.put(Byte.TYPE, ByteDeserializer::new); DESERIALIZERS.put(Calendar.class, CalendarDeserializer::new); DESERIALIZERS.put(Character.TYPE, CharDeserializer::new); DESERIALIZERS.put(Character.class, CharDeserializer::new); DESERIALIZERS.put(Date.class, DateDeserializer::new); DESERIALIZERS.put(Double.class, DoubleDeserializer::new); DESERIALIZERS.put(Double.TYPE, DoubleDeserializer::new); DESERIALIZERS.put(Duration.class, DurationDeserializer::new); DESERIALIZERS.put(Float.class, FloatDeserializer::new); DESERIALIZERS.put(Float.TYPE, FloatDeserializer::new); DESERIALIZERS.put(GregorianCalendar.class, CalendarDeserializer::new); DESERIALIZERS.put(Instant.class, InstantDeserializer::new); DESERIALIZERS.put(Integer.class, IntegerDeserializer::new); DESERIALIZERS.put(Integer.TYPE, IntegerDeserializer::new); DESERIALIZERS.put(LocalDate.class, LocalDateDeserializer::new); DESERIALIZERS.put(LocalDateTime.class, LocalDateTimeDeserializer::new); DESERIALIZERS.put(LocalTime.class, LocalTimeDeserializer::new); DESERIALIZERS.put(Long.class, LongDeserializer::new); DESERIALIZERS.put(Long.TYPE, LongDeserializer::new); DESERIALIZERS.put(MonthDay.class, MonthDayTypeDeserializer::new); DESERIALIZERS.put(Number.class, NumberDeserializer::new); DESERIALIZERS.put(OffsetDateTime.class, OffsetDateTimeDeserializer::new); DESERIALIZERS.put(OffsetTime.class, OffsetTimeDeserializer::new); DESERIALIZERS.put(Path.class, PathDeserializer::new); DESERIALIZERS.put(Period.class, PeriodDeserializer::new); DESERIALIZERS.put(Short.class, ShortDeserializer::new); DESERIALIZERS.put(Short.TYPE, ShortDeserializer::new); DESERIALIZERS.put(String.class, StringDeserializer::new); DESERIALIZERS.put(SimpleTimeZone.class, TimeZoneDeserializer::new); DESERIALIZERS.put(TimeZone.class, TimeZoneDeserializer::new); DESERIALIZERS.put(URI.class, UriDeserializer::new); DESERIALIZERS.put(URL.class, UrlDeserializer::new); DESERIALIZERS.put(UUID.class, UuidDeserializer::new); if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) { DESERIALIZERS.put(XMLGregorianCalendar.class, XmlGregorianCalendarDeserializer::new); } DESERIALIZERS.put(YearMonth.class, YearMonthTypeDeserializer::new); DESERIALIZERS.put(ZonedDateTime.class, ZonedDateTimeDeserializer::new); DESERIALIZERS.put(ZoneId.class, ZoneIdDeserializer::new); DESERIALIZERS.put(ZoneOffset.class, ZoneOffsetDeserializer::new); if (isClassAvailable("java.sql.Date")) { DESERIALIZERS.put(java.sql.Date.class, SqlDateDeserializer::new); DESERIALIZERS.put(Timestamp.class, SqlTimestampDeserializer::new); } OPTIONAL_TYPES.put(OptionalLong.class, Long.class); OPTIONAL_TYPES.put(OptionalInt.class, Integer.class); OPTIONAL_TYPES.put(OptionalDouble.class, Double.class); } private TypeDeserializers() { throw new IllegalStateException("Utility classes cannot be instantiated"); } /** * Return deserializer for the given type. * * @param clazz type to create deserializer for * @param customization type customization * @param properties config properties * @param delegate delegate to be called by the created deserializer * @param events expected parser events at the beginning when deserializing the type * @return type deserializer */ public static ModelDeserializer getTypeDeserializer(Class clazz, Customization customization, JsonbConfigProperties properties, ModelDeserializer delegate, Set events) { JsonParser.Event[] eventArray = events.toArray(new JsonParser.Event[0]); if (OPTIONAL_TYPES.containsKey(clazz)) { Class optionalType = OPTIONAL_TYPES.get(clazz); TypeDeserializerBuilder builder = new TypeDeserializerBuilder(optionalType, customization, properties, JustReturn.instance()); ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(optionalType).apply(builder)); PositionChecker positionChecker = new PositionChecker(valueExtractor, clazz, eventArray); if (OptionalLong.class.equals(clazz)) { return new OptionalLongDeserializer(positionChecker, delegate); } else if (OptionalInt.class.equals(clazz)) { return new OptionalIntDeserializer(positionChecker, delegate); } else if (OptionalDouble.class.equals(clazz)) { return new OptionalDoubleDeserializer(positionChecker, delegate); } else { throw new JsonbException("Unsupported Optional type for deserialization: " + clazz); } } TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, properties, delegate); if (DESERIALIZERS.containsKey(clazz)) { ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(clazz).apply(builder)); return new NullCheckDeserializer(new PositionChecker(valueExtractor, clazz, eventArray), delegate); } if (JsonValue.class.equals(clazz)) { return new JsonValueDeserializer(builder, JsonValue.NULL); } else if (JsonValue.class.isAssignableFrom(clazz)) { return new JsonValueDeserializer(builder, null); } ModelDeserializer deserializer = assignableCases(builder, eventArray); if (deserializer != null) { return new NullCheckDeserializer(deserializer, delegate); } return null; } private static ModelDeserializer assignableCases(TypeDeserializerBuilder builder, JsonParser.Event[] checker) { if (Enum.class.isAssignableFrom(builder.getClazz())) { return new PositionChecker(new ValueExtractor(new EnumDeserializer(builder)), builder.getClazz(), checker); } else if (Object.class.equals(builder.getClazz())) { return new ObjectTypeDeserializer(builder); } return null; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/UriDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.net.URI; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link URI} type. */ class UriDeserializer extends TypeDeserializer { UriDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return URI.create(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/UrlDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.net.MalformedURLException; import java.net.URL; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link URL} type. */ class UrlDeserializer extends TypeDeserializer { UrlDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { URL url = null; try { url = new URL(value); } catch (MalformedURLException e) { e.printStackTrace(); } return url; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/UuidDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.util.UUID; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link UUID} type. */ class UuidDeserializer extends TypeDeserializer { UuidDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return UUID.fromString(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/XmlGregorianCalendarDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link XMLGregorianCalendar} type. */ class XmlGregorianCalendarDeserializer extends AbstractDateDeserializer { private static final LocalTime ZERO_LOCAL_TIME = LocalTime.parse("00:00:00"); private final Calendar calendarTemplate; private final DatatypeFactory datatypeFactory; XmlGregorianCalendarDeserializer(TypeDeserializerBuilder builder) { super(builder); this.calendarTemplate = new GregorianCalendar(); this.calendarTemplate.clear(); this.calendarTemplate.setTimeZone(TimeZone.getTimeZone(UTC)); try { this.datatypeFactory = DatatypeFactory.newInstance(); } catch (DatatypeConfigurationException e) { throw new JsonbException(Messages.getMessage(MessageKeys.DATATYPE_FACTORY_CREATION_FAILED), e); } } @Override protected XMLGregorianCalendar fromInstant(Instant instant) { final GregorianCalendar calendar = (GregorianCalendar) calendarTemplate.clone(); calendar.setTimeInMillis(instant.toEpochMilli()); return datatypeFactory.newXMLGregorianCalendar(calendar); } @Override protected XMLGregorianCalendar parseDefault(String jsonValue, Locale locale) { DateTimeFormatter formatter = jsonValue.contains("T") ? DateTimeFormatter.ISO_DATE_TIME : DateTimeFormatter.ISO_DATE; return parseWithFormatter(jsonValue, formatter.withLocale(locale)); } @Override protected XMLGregorianCalendar parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { final TemporalAccessor parsed = formatter.parse(jsonValue); LocalTime time = parsed.query(TemporalQueries.localTime()); ZoneId zone = parsed.query(TemporalQueries.zone()); if (zone == null) { zone = UTC; } if (time == null) { time = ZERO_LOCAL_TIME; } ZonedDateTime result = LocalDate.from(parsed).atTime(time).atZone(zone); return datatypeFactory.newXMLGregorianCalendar(GregorianCalendar.from(result)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/YearMonthTypeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Deserializer of the {@link YearMonth} type. */ class YearMonthTypeDeserializer extends AbstractDateDeserializer { private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM").withZone(UTC); YearMonthTypeDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override protected YearMonth fromInstant(Instant instant) { return YearMonth.from(instant.atZone(UTC)); } @Override protected YearMonth parseDefault(String jsonValue, Locale locale) { return YearMonth.parse(jsonValue, DEFAULT_FORMAT.withLocale(locale)); } @Override protected YearMonth parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return YearMonth.parse(jsonValue, formatter); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneIdDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.time.ZoneId; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link ZoneId} type. */ class ZoneIdDeserializer extends TypeDeserializer { ZoneIdDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return ZoneId.of(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneOffsetDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.lang.reflect.Type; import java.time.ZoneOffset; import org.eclipse.yasson.internal.DeserializationContextImpl; /** * Deserializer of the {@link ZoneOffset} type. */ class ZoneOffsetDeserializer extends TypeDeserializer { ZoneOffsetDeserializer(TypeDeserializerBuilder builder) { super(builder); } @Override Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) { return ZoneOffset.of(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/deserializer/types/ZonedDateTimeDeserializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.deserializer.types; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.logging.Logger; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Deserializer of the {@link ZonedDateTime} type. */ class ZonedDateTimeDeserializer extends AbstractDateDeserializer { private static final Logger LOGGER = Logger.getLogger(ZonedDateTimeDeserializer.class.getName()); ZonedDateTimeDeserializer(TypeDeserializerBuilder builder) { super(builder); } /** * fromInstant is called only in case {@link jakarta.json.bind.annotation.JsonbDateFormat} is TIME_IN_MILLIS, * which doesn't make much sense for usage with ZonedDateTime. */ @Override protected ZonedDateTime fromInstant(Instant instant) { LOGGER.warning(Messages.getMessage(MessageKeys.OFFSET_DATE_TIME_FROM_MILLIS, ZonedDateTime.class.getSimpleName(), UTC)); return ZonedDateTime.ofInstant(instant, UTC); } @Override protected ZonedDateTime parseDefault(String jsonValue, Locale locale) { return ZonedDateTime.parse(jsonValue, DateTimeFormatter.ISO_ZONED_DATE_TIME.withLocale(locale)); } @Override protected ZonedDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) { return ZonedDateTime.parse(jsonValue, getZonedFormatter(formatter)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.math.BigDecimal; import java.math.BigInteger; import jakarta.json.JsonArray; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.spi.JsonProvider; /** * Builds {@link JsonArray}. Delegates to {@link jakarta.json.JsonArrayBuilder}. */ class JsonArrayBuilder extends JsonStructureBuilder { private final jakarta.json.JsonArrayBuilder arrayBuilder; /** * Create instance with cached provider. * * @param provider Json provider to create JsonArrayBuilder on. */ JsonArrayBuilder(JsonProvider provider) { this.arrayBuilder = provider.createArrayBuilder(); } @Override JsonArray build() { return arrayBuilder.build(); } @Override void write(JsonValue value) { arrayBuilder.add(value); } @Override void write(String value) { arrayBuilder.add(value); } @Override void write(BigDecimal value) { arrayBuilder.add(value); } @Override void write(BigInteger value) { arrayBuilder.add(value); } @Override void write(int value) { arrayBuilder.add(value); } @Override void write(long value) { arrayBuilder.add(value); } @Override void write(double value) { arrayBuilder.add(value); } @Override void write(boolean value) { arrayBuilder.add(value); } @Override void writeNull() { arrayBuilder.addNull(); } @Override void put(JsonStructure structure) { arrayBuilder.add(structure); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.util.Iterator; import jakarta.json.JsonArray; import jakarta.json.JsonString; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Iterates over {@link JsonArray}. */ public class JsonArrayIterator extends JsonStructureIterator { private final Iterator valueIterator; private JsonValue currentValue; /** * Creates new array iterator. * * @param jsonArray json array */ public JsonArrayIterator(JsonArray jsonArray) { this.valueIterator = jsonArray.iterator(); } /** * After {@link JsonParser.Event} END_ARRAY is returned from next() iterator is removed from the stack. * * @return always true */ @Override public boolean hasNext() { return true; } @Override public JsonParser.Event next() { if (valueIterator.hasNext()) { currentValue = valueIterator.next(); return getValueEvent(currentValue); } return JsonParser.Event.END_ARRAY; } @Override JsonValue getValue() { return currentValue; } @Override JsonbException createIncompatibleValueError() { return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY, getValue().getValueType())); } @Override String getString() { if (currentValue instanceof JsonString) { return ((JsonString) currentValue).getString(); } return currentValue.toString(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.Deque; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.spi.JsonProvider; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Adapter for {@link JsonGenerator}, that builds a {@link JsonStructure} content tree instead of JSON text. * * Yasson and jsonb API components are using {@link JsonGenerator} as its output API. * This adapter allows serialization of java content tree into {@link JsonStructure} using same components * as when generating JSON text. */ public class JsonGeneratorToStructureAdapter implements JsonGenerator { private final Deque builders; private JsonStructure root; private final JsonProvider provider; /** * Default constructor, jsonp builders are created internally. * * @param provider Cached json provider to create builders on. */ public JsonGeneratorToStructureAdapter(JsonProvider provider) { this.builders = new ArrayDeque<>(); this.provider = provider; } @Override public JsonGenerator writeStartObject() { builders.push(new JsonObjectBuilder(provider)); return this; } @Override public JsonGenerator writeStartObject(String name) { getJsonObjectBuilder(name).writeKey(name); builders.push(new JsonObjectBuilder(provider)); return this; } @Override public JsonGenerator writeKey(String name) { getJsonObjectBuilder(name).writeKey(name); return this; } @Override public JsonGenerator writeStartArray() { builders.push(new JsonArrayBuilder(provider)); return this; } @Override public JsonGenerator writeStartArray(String name) { getJsonObjectBuilder(name).writeKey(name); builders.push(new JsonArrayBuilder(provider)); return this; } @Override public JsonGenerator write(String name, JsonValue value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, String value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, BigInteger value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, BigDecimal value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, int value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, long value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, double value) { getJsonObjectBuilder(name).write(name, value); return this; } @Override public JsonGenerator write(String name, boolean value) { getJsonObjectBuilder(name).write(name, value); return this; } private JsonObjectBuilder getJsonObjectBuilder(String keyName) { JsonStructureBuilder current = builders.peek(); if (!(current instanceof JsonObjectBuilder)) { throw new JsonbException(Messages.getMessage( MessageKeys.INTERNAL_ERROR, "Can't write key [" + keyName + "] into " + current.getClass() + "because " + current.getClass() + " is not an instance of " + JsonObjectBuilder.class)); } return (JsonObjectBuilder) current; } @Override public JsonGenerator writeNull(String name) { getJsonObjectBuilder(name).writeNull(name); return this; } @Override public JsonGenerator writeEnd() { JsonStructureBuilder builder = builders.pop(); JsonStructure structure = builder.build(); if (builders.isEmpty()) { this.root = structure; } else { builders.peek().put(structure); } return this; } @Override public JsonGenerator write(JsonValue value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(String value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(BigDecimal value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(BigInteger value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(int value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(long value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(double value) { builders.peek().write(value); return this; } @Override public JsonGenerator write(boolean value) { builders.peek().write(value); return this; } @Override public JsonGenerator writeNull() { builders.peek().writeNull(); return this; } @Override public void close() { //noop } @Override public void flush() { //noop } /** * Root structure wrapping all data. * * @return root JsonStructure. */ public JsonStructure getRootStructure() { return root; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.math.BigDecimal; import java.math.BigInteger; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.spi.JsonProvider; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Builds {@link jakarta.json.JsonObject} delegates to {@link jakarta.json.JsonObjectBuilder}, caches key when * written without a value. */ class JsonObjectBuilder extends JsonStructureBuilder { private final jakarta.json.JsonObjectBuilder builder; private String nextKey; /** * Create instance with cached provider. * * @param provider Json provider to create JsonObjectBuilder on. */ JsonObjectBuilder(JsonProvider provider) { this.builder = provider.createObjectBuilder(); } @Override JsonStructure build() { return builder.build(); } @Override void put(JsonStructure structure) { builder.add(getNextKey(), structure); } /** * Puts another {@link JsonStructure} into current using provided key. * * @param name key to put JsonStructure under. * @param structure JsonStructure to put. */ void put(String name, JsonStructure structure) { builder.add(name, structure); } @Override void write(JsonValue value) { builder.add(getNextKey(), value); } @Override void write(String value) { builder.add(getNextKey(), value); } @Override void write(BigDecimal value) { builder.add(getNextKey(), value); } @Override void write(BigInteger value) { builder.add(getNextKey(), value); } @Override void write(int value) { builder.add(getNextKey(), value); } @Override void write(long value) { builder.add(getNextKey(), value); } @Override void write(double value) { builder.add(getNextKey(), value); } @Override void write(boolean value) { builder.add(getNextKey(), value); } @Override void writeNull() { builder.addNull(getNextKey()); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, JsonValue value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, String value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, BigDecimal value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, BigInteger value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, int value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, long value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, double value) { builder.add(name, value); } /** * Write a key-value pair into current {@link jakarta.json.JsonObject}. * * @param name Key name to write value with. * @param value A value to write. */ void write(String name, boolean value) { builder.add(name, value); } /** * Write a null into current {@link jakarta.json.JsonObject} with a given key. * * @param name Key name to write null with. */ void writeNull(String name) { builder.addNull(name); } /** * Store a key for putting next value into built {@link jakarta.json.JsonObject}. * * @param key Key to store. */ void writeKey(String key) { this.nextKey = key; } private String getNextKey() { if (nextKey == null) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Can't write a value without key name")); } String key = nextKey; nextKey = null; return key; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.util.Iterator; import jakarta.json.JsonObject; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Iterates over {@link JsonObject} managing internal state. */ public class JsonObjectIterator extends JsonStructureIterator { /** * Location pointer. */ public enum State { /** * Start of the object. */ START, /** * Property key name. */ KEY, /** * Property value. */ VALUE, /** * End of the object. */ END } private final JsonObject jsonObject; private final Iterator keyIterator; private String currentKey; private State state = State.START; JsonObjectIterator(JsonObject jsonObject) { this.jsonObject = jsonObject; this.keyIterator = jsonObject.keySet().iterator(); } private void nextKey() { if (!keyIterator.hasNext()) { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Object is empty")); } currentKey = keyIterator.next(); } @Override public JsonParser.Event next() { switch (state) { case START: if (keyIterator.hasNext()) { nextKey(); setState(JsonObjectIterator.State.KEY); return JsonParser.Event.KEY_NAME; } else { setState(State.END); return JsonParser.Event.END_OBJECT; } case KEY: setState(JsonObjectIterator.State.VALUE); JsonValue value = getValue(); return getValueEvent(value); case VALUE: if (keyIterator.hasNext()) { nextKey(); setState(JsonObjectIterator.State.KEY); return JsonParser.Event.KEY_NAME; } setState(State.END); return JsonParser.Event.END_OBJECT; default: throw new JsonbException("Illegal state"); } } @Override public boolean hasNext() { //From the perspective of JsonParser not finished until END_OBJECT is being read. return state != State.END; } /** * {@link JsonValue} for current key. * * @return Current JsonValue. */ public JsonValue getValue() { if (state == State.START && currentKey == null) { return jsonObject; } return jsonObject.get(currentKey); } @Override String getString() { if (state == JsonObjectIterator.State.KEY) { return currentKey; } return super.getString(); } @Override JsonbException createIncompatibleValueError() { return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT, getValue().getValueType(), currentKey)); } private void setState(State state) { this.state = state; } /** * Current key this iterator is pointing at. * * @return Current key. */ public String getKey() { return currentKey; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.math.BigDecimal; import java.math.BigInteger; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; /** * Grouping interface for {@link jakarta.json.JsonObject} and {@link jakarta.json.JsonArray} generation. */ abstract class JsonStructureBuilder { /** * Build and get constructed {@link JsonStructure}. * * @return JsonStructure result. */ abstract JsonStructure build(); /** * Puts another {@link JsonStructure} into current. If current is {@link jakarta.json.JsonObject} than last written * key is used. * * @param structure */ abstract void put(JsonStructure structure); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(JsonValue value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(String value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(BigDecimal value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(BigInteger value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(int value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(long value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(double value); /** * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. * * @param value A value to write. */ abstract void write(boolean value); /** * Write null into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key * by {@link JsonObjectBuilder#writeKey(String)} is used. */ abstract void writeNull(); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.util.Iterator; import jakarta.json.JsonString; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Iterates over {@link jakarta.json.JsonStructure}. */ abstract class JsonStructureIterator implements Iterator { /** * Get current {@link JsonValue}, that the parser is pointing on. * * @return JsonValue result. */ abstract JsonValue getValue(); /** * Creates an exception for throwing in case of current value type is not compatible with * called getter return type. * * @return JsonbException with error description. */ abstract JsonbException createIncompatibleValueError(); /** * Check the type of current {@link JsonValue} and return a string representing a value. * * @return String value for current JsonValue */ String getString() { JsonValue value = getValue(); if (value instanceof JsonString) { return ((JsonString) value).getString(); } else { return value.toString(); } } /** * Convert {@link JsonValue} type to {@link JsonParser.Event}. * * @param value JsonValue * @return JsonParser event */ JsonParser.Event getValueEvent(JsonValue value) { switch (value.getValueType()) { case NUMBER: return JsonParser.Event.VALUE_NUMBER; case STRING: case TRUE: case FALSE: return JsonParser.Event.VALUE_STRING; case OBJECT: return JsonParser.Event.START_OBJECT; case ARRAY: return JsonParser.Event.START_ARRAY; case NULL: return JsonParser.Event.VALUE_NULL; default: throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "unknown json value: " + value.getValueType())); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java ================================================ /* * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2026 Contributors to the Eclipse Foundation. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.jsonstructure; import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.Deque; import jakarta.json.JsonArray; import jakarta.json.JsonNumber; import jakarta.json.JsonObject; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text. * * Yasson and jsonb API components are using {@link JsonParser} as its input API. * This adapter allows deserialization of {@link JsonStructure} into java content tree using same components * as when parsing JSON text. */ public class JsonStructureToParserAdapter implements JsonParser { private Deque iterators = new ArrayDeque<>(); private final JsonStructure rootStructure; /** * Creates new {@link JsonStructure} parser. * * @param structure json structure */ public JsonStructureToParserAdapter(JsonStructure structure) { this.rootStructure = structure; } @Override public boolean hasNext() { return iterators.peek().hasNext(); } @Override public Event next() { if (iterators.isEmpty()) { if (rootStructure instanceof JsonObject) { iterators.push(new JsonObjectIterator((JsonObject) rootStructure)); return Event.START_OBJECT; } else if (rootStructure instanceof JsonArray) { iterators.push(new JsonArrayIterator((JsonArray) rootStructure)); return Event.START_ARRAY; } } JsonStructureIterator current = iterators.peek(); Event next = current.next(); if (next == Event.START_OBJECT) { iterators.push(new JsonObjectIterator((JsonObject) current.getValue())); } else if (next == Event.START_ARRAY) { iterators.push(new JsonArrayIterator((JsonArray) current.getValue())); } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) { iterators.pop(); } return next; } @Override public Event currentEvent() { return iterators.peek().getValueEvent(getValue()); } @Override public String getString() { return iterators.peek().getString(); } @Override public boolean isIntegralNumber() { return getJsonNumberValue().isIntegral(); } @Override public int getInt() { return getJsonNumberValue().intValueExact(); } @Override public long getLong() { return getJsonNumberValue().longValueExact(); } @Override public BigDecimal getBigDecimal() { return getJsonNumberValue().bigDecimalValue(); } @Override public JsonValue getValue() { return iterators.peek().getValue(); } @Override public JsonObject getObject() { JsonStructureIterator current = iterators.peek(); if (current instanceof JsonObjectIterator) { //Remove child iterator as getObject() method contract says iterators.pop(); return current.getValue().asJsonObject(); } else { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context")); } } @Override public JsonArray getArray() { JsonStructureIterator current = iterators.peek(); if (current instanceof JsonArrayIterator) { iterators.pop(); return getValue().asJsonArray(); } else { throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of array context")); } } private JsonNumber getJsonNumberValue() { JsonStructureIterator iterator = iterators.peek(); JsonValue value = iterator.getValue(); if (value.getValueType() != JsonValue.ValueType.NUMBER) { throw iterator.createIncompatibleValueError(); } return (JsonNumber) value; } @Override public JsonLocation getLocation() { throw new JsonbException("Operation not supported"); } @Override public void skipArray() { if (!iterators.isEmpty()) { JsonStructureIterator current = iterators.peek(); if (current instanceof JsonArrayIterator) { iterators.pop(); } } } @Override public void skipObject() { if (!iterators.isEmpty()) { JsonStructureIterator current = iterators.peek(); if (current instanceof JsonObjectIterator) { iterators.pop(); } } } @Override public void close() { //noop } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/AnnotationTarget.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import org.eclipse.yasson.internal.JsonbNumberFormatter; /** * Represents the place in which a JSON annotation is applied. Some business functionalities are different based on whether * annotation (e.g. * {@link jakarta.json.bind.annotation.JsonbTransient}, {@link JsonbNumberFormatter}, etc * .) is being applied on * getter method, setter method or directly on the property. */ public enum AnnotationTarget { /** * Indicates annotation has been applied on class level. */ CLASS, /** * Indicates annotation has been applied on property level. */ PROPERTY, /** * Indicates annotation has been applied on the getter method of the property. */ GETTER, /** * Indicates annotation has been applied on the setter method of the property. */ SETTER } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/ClassModel.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import jakarta.json.bind.config.PropertyNamingStrategy; import org.eclipse.yasson.internal.ClassMultiReleaseExtension; import org.eclipse.yasson.internal.ReflectionUtils; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.StrategiesProvider; /** * A model for Java class. */ public class ClassModel { private final Class clazz; private final ClassCustomization classCustomization; private final ClassModel parentClassModel; private final AtomicBoolean isInitialized = new AtomicBoolean(false); private Constructor defaultConstructor; /** * A map of all class properties, including properties from superclasses. Used to access by name. */ private Map properties; /** * Sorted properties according to sorting strategy. Used for serialization property ordering. */ private PropertyModel[] sortedProperties; private final PropertyNamingStrategy propertyNamingStrategy; /** * Gets a property model by default (non customized) name. * * @param name A name as parsed from field / getter / setter without annotation customizing. * @return Property model. */ public PropertyModel getPropertyModel(String name) { return properties.get(name); } /** * Create instance of class model. * * @param clazz Class to model. * @param customization Customization of the class parsed from annotations. * @param parentClassModel Class model of parent class. * @param propertyNamingStrategy Property naming strategy. */ public ClassModel(Class clazz, ClassCustomization customization, ClassModel parentClassModel, PropertyNamingStrategy propertyNamingStrategy) { this.clazz = clazz; this.classCustomization = customization; this.parentClassModel = parentClassModel; this.propertyNamingStrategy = propertyNamingStrategy; setProperties(new ArrayList<>()); } /** * Search for field in this class model and superclasses of its class. * * @param jsonReadName name as it appears in JSON during reading. * @return PropertyModel if found. */ public PropertyModel findPropertyModelByJsonReadName(String jsonReadName) { Objects.requireNonNull(jsonReadName); return searchProperty(this, jsonReadName); } private PropertyModel searchProperty(ClassModel classModel, String jsonReadName) { //Standard javabean properties without overridden name (most of the cases) final PropertyModel result = classModel.getPropertyModel(jsonReadName); if (result != null && result.getPropertyName().equals(result.getReadName())) { return result; } //Search for overridden name on setter with @JsonbProperty annotation for (PropertyModel propertyModel : properties.values()) { if (equalsReadName(jsonReadName, propertyModel)) { return propertyModel; } } //property not found return null; } /** * Check if name is equal according to property strategy. * In case of {@link StrategiesProvider#CASE_INSENSITIVE_STRATEGY} ignore case. * User can provide own strategy implementation, cast to custom interface is not an option. * * @return True if names are equal. */ private boolean equalsReadName(String jsonName, PropertyModel propertyModel) { final String propertyReadName = propertyModel.getReadName(); if (propertyNamingStrategy == StrategiesProvider.CASE_INSENSITIVE_STRATEGY) { return jsonName.equalsIgnoreCase(propertyReadName); } return jsonName.equals(propertyReadName); } /** * Gets type. * * @return Type. */ public Class getType() { return clazz; } /** * Introspected customization for a class. * * @return Immutable class customization. */ public ClassCustomization getClassCustomization() { return classCustomization; } /** * Class model of parent class if present. * * @return class model of a parent */ public ClassModel getParentClassModel() { return parentClassModel; } /** * Get sorted class properties copy, combination of field and its getter / setter, javabeans alike. * * @return sorted class properties. */ public PropertyModel[] getSortedProperties() { return sortedProperties; } /** * Sets parsed properties of the class. * * @param parsedProperties class properties */ public void setProperties(List parsedProperties) { sortedProperties = parsedProperties.toArray(new PropertyModel[] {}); this.properties = parsedProperties.stream().collect(Collectors.toMap(PropertyModel::getPropertyName, (mod) -> mod)); } /** * Get class properties copy, combination of field and its getter / setter, javabeans alike. * * @return class properties. */ public Map getProperties() { return Collections.unmodifiableMap(properties); } /** * Default no argument constructor of the class used for deserialization. * * @return default constructor */ public Constructor getDefaultConstructor() { // Lazy-loads the default constructor to avoid Java 9+ "Illegal reflective access" warnings where possible. // Example: Deserialization into Map won't use this constructor, and therefore never needs to call this method. // Note: Null is a valid result and needs to be cached. if (!isInitialized.get()) { if (ClassMultiReleaseExtension.isRecord(clazz)) { //No default constructor should be used in case of records defaultConstructor = null; } else { defaultConstructor = ReflectionUtils.getDefaultConstructor(clazz, false); } isInitialized.set(true); } return defaultConstructor; } @Override public String toString() { return "ClassModel{" + "clazz=" + clazz + '}'; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.reflect.Executable; import java.lang.reflect.Parameter; import java.lang.reflect.Type; import org.eclipse.yasson.internal.AnnotationIntrospector; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.model.customization.CreatorCustomization; /** * Parameter for creator constructor / method model. */ public class CreatorModel { private final String name; private final Type type; private final CreatorCustomization creatorCustomization; /** * Creates a new instance. * @param name Parameter name * @param parameter constructor parameter * @param executable creator executable * @param context jsonb context */ public CreatorModel(String name, Parameter parameter, Executable executable, JsonbContext context) { this.name = name; this.type = parameter.getParameterizedType(); AnnotationIntrospector annotationIntrospector = context.getAnnotationIntrospector(); JsonbAnnotatedElement annotated = new JsonbAnnotatedElement<>(parameter); boolean required = context.getAnnotationIntrospector().requiredParameters(executable, annotated); JsonbNumberFormatter constructorNumberFormatter = context.getAnnotationIntrospector() .getConstructorNumberFormatter(annotated); JsonbDateFormatter constructorDateFormatter = context.getAnnotationIntrospector().getConstructorDateFormatter(annotated); DeserializerBinding deserializerBinding = annotationIntrospector.getDeserializerBinding(parameter); AdapterBinding adapterBinding = annotationIntrospector.getAdapterBinding(parameter); final JsonbAnnotatedElement> clsElement = annotationIntrospector.collectAnnotations(parameter.getType()); deserializerBinding = deserializerBinding == null ? annotationIntrospector.getDeserializerBinding(clsElement) : deserializerBinding; adapterBinding = adapterBinding == null ? annotationIntrospector.getAdapterBinding(clsElement) : adapterBinding; this.creatorCustomization = CreatorCustomization.builder() .adapterBinding(adapterBinding) .deserializerBinding(deserializerBinding) .serializerBinding(annotationIntrospector.getSerializerBinding(clsElement)) .numberFormatter(constructorNumberFormatter) .dateFormatter(constructorDateFormatter) .required(required) .build(); } /** * Gets parameter name. * * @return Parameter name. */ public String getName() { return name; } public CreatorCustomization getCustomization() { return creatorCustomization; } /** * Gets parameter type. * * @return Parameter type. */ public Type getType() { return type; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java ================================================ /* * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Optional; /** * Annotation holder for classes, superclasses, interfaces, fields, getters and setters. * * @param annotated element */ public class JsonbAnnotatedElement { private final Map, LinkedList>> annotations = new HashMap<>(4); private final T element; /** * Creates a new instance. * * @param element Element. */ public JsonbAnnotatedElement(T element) { for (Annotation ann : element.getAnnotations()) { if (element instanceof Class) { putAnnotation(ann, false, (Class) element); } else { putAnnotation(ann, false, null); } } this.element = element; } /** * Gets element. * * @return Element. */ public T getElement() { return element; } /** * Get an annotation by type. * * @param Type of annotation * @param annotationClass Type of annotation * @return Annotation by passed type */ public Optional getAnnotation(Class annotationClass) { return Optional.ofNullable(annotations.get(annotationClass)) .map(LinkedList::getFirst) .map(AnnotationWrapper::getAnnotation) .map(annotationClass::cast); } public LinkedList> getAnnotations(Class annotationClass) { return annotations.getOrDefault(annotationClass, new LinkedList<>()); } @SuppressWarnings("unchecked") public AnnotationWrapper getAnnotationWrapper(Class annotationClass) { return (AnnotationWrapper) annotations.get(annotationClass).getFirst(); } public Annotation[] getAnnotations() { return annotations.values().stream() .flatMap(Collection::stream) .map(AnnotationWrapper::getAnnotation) .toArray(Annotation[]::new); } /** * Adds annotation. * * @param annotation Annotation to add. * @param definedType */ public void putAnnotation(Annotation annotation, boolean inherited, Class definedType) { // if (annotations.containsKey(annotation.annotationType())) { // throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, // "Annotation already present: " + annotation)); // } // annotations.put(annotation.annotationType(), new AnnotationWrapper(annotation, inherited)); annotations.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()) .add(new AnnotationWrapper(annotation, inherited, definedType)); } public void putAnnotationWrapper(AnnotationWrapper annotationWrapper) { annotations.computeIfAbsent(annotationWrapper.getAnnotation().annotationType(), aClass -> new LinkedList<>()) .add(annotationWrapper); } public static final class AnnotationWrapper { private final T annotation; private final boolean inherited; private final Class definedType; public AnnotationWrapper(T annotation, boolean inherited, Class definedType) { this.annotation = annotation; this.inherited = inherited; this.definedType = definedType; } public T getAnnotation() { return annotation; } public boolean isInherited() { return inherited; } public Class getDefinedType() { return definedType; } @Override public String toString() { return definedType.getName(); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/JsonbCreator.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Object holding reference to Constructor / Method for custom object creation. */ public class JsonbCreator { private final Executable executable; private final CreatorModel[] params; /** * Creates a new instance. * * @param executable Executable. * @param creatorModels Parameters. */ public JsonbCreator(Executable executable, CreatorModel[] creatorModels) { this.executable = executable; this.params = creatorModels; } /** * Create instance by either constructor or factory method, with provided parameter values and a Class to call on. * * @param params parameters to be passed into constructor / factory method * @param on class to call onto * @param Type of class / instance * @return instance */ @SuppressWarnings("unchecked") public T call(Object[] params, Class on) { try { if (executable instanceof Constructor) { return ((Constructor) executable).newInstance(params); } else { return (T) ((Method) executable).invoke(on, params); } } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new JsonbException(Messages.getMessage(MessageKeys.ERROR_CALLING_JSONB_CREATOR, on), e); } } /** * True if param name is one of creator params. * * @param paramName Param name to check. * @return True if found. */ public boolean contains(String paramName) { return findByName(paramName) != null; } /** * Find creator parameter by name. * * @param paramName parameter name as it appear in json document. * @return Creator parameter. */ public CreatorModel findByName(String paramName) { for (CreatorModel param : params) { if (param.getName().equals(paramName)) { return param; } } return null; } /** * Parameters of this creator. * * @return Parameters. */ public CreatorModel[] getParams() { return params; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.invoke.MethodHandles; class ModulesUtil { private ModulesUtil() { } static MethodHandles.Lookup lookup(){ return MethodHandles.publicLookup(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/Property.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import jakarta.json.bind.JsonbException; /** * Property of a class, field, getter and setter methods (javabean alike). * Used during class model initialization, than dereferenced. */ public class Property { private final String name; private final JsonbAnnotatedElement> declaringClassElement; private JsonbAnnotatedElement fieldElement; private JsonbAnnotatedElement getterElement; private JsonbAnnotatedElement setterElement; /** * Create instance of property. * * @param name not null * @param declaringClassModel Class model for a class declaring property. */ public Property(String name, JsonbAnnotatedElement> declaringClassModel) { this.name = name; this.declaringClassElement = declaringClassModel; } /** * Name of a property, java bean convention. * * @return name */ public String getName() { return name; } /** * {@link Field} representing property if any. * * @return field if present */ public Field getField() { if (fieldElement == null) { return null; } return fieldElement.getElement(); } /** * @param field field not null */ public void setField(Field field) { this.fieldElement = new JsonbAnnotatedElement<>(field); } /** * {@link Method} representing getter of a property if any. * * @return getter if present */ public Method getGetter() { if (getterElement == null) { return null; } return getterElement.getElement(); } /** * @param getter not null */ public void setGetter(Method getter) { this.getterElement = new JsonbAnnotatedElement<>(getter); } /** * {@link Method} representing setter of a property if any. * * @return setter if present */ public Method getSetter() { if (setterElement == null) { return null; } return setterElement.getElement(); } /** * @param setter setter not null */ public void setSetter(Method setter) { this.setterElement = new JsonbAnnotatedElement<>(setter); } /** * Class element with annotation under construction for declaring class of this property. * This ClassModel is not fully initialized yet. * * @return ClassModel */ public JsonbAnnotatedElement> getDeclaringClassElement() { return declaringClassElement; } /** * Extracts type from first not null element: * Field, Getter, Setter. * * @return type of a property */ public Type getPropertyType() { if (getField() != null) { return getField().getGenericType(); } else if (getGetter() != null) { return getGetterType(); } else if (getSetter() != null) { return getSetterType(); } throw new JsonbException("Empty property: " + name); } Type getGetterType() { if (getGetter() != null) { return getGetter().getGenericReturnType(); } return null; } Type getSetterType() { Type[] genericParameterTypes = getSetter().getGenericParameterTypes(); if (genericParameterTypes.length != 1) { throw new JsonbException("Invalid count of arguments for setter: " + getSetter()); } return genericParameterTypes[0]; } /** * Element with field and its annotations. * * @return field with annotations */ public JsonbAnnotatedElement getFieldElement() { return fieldElement; } /** * Element with getter and its annotations. * * @return getter with annotations */ public JsonbAnnotatedElement getGetterElement() { return getterElement; } /** * Element with setter and its annotations. * * @return setter with annotations */ public JsonbAnnotatedElement getSetterElement() { return setterElement; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.EnumSet; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.bind.config.PropertyVisibilityStrategy; import org.eclipse.yasson.internal.AnnotationIntrospector; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.SerializerBinding; import org.eclipse.yasson.internal.model.customization.PropertyCustomization; /** * A model for class property. * Property is JavaBean alike meta information field / getter / setter of a property in class. */ public final class PropertyModel implements Comparable { private static final MethodHandles.Lookup LOOKUP = ModulesUtil.lookup(); /** * Field propertyName as in class by java bean convention. */ private final String propertyName; /** * Calculated name to be used when reading json document. */ private final String readName; /** * Calculated name to be used when writing json document. */ private final String writeName; /** * Field propertyType. */ private final Type propertyType; /** * Model of the class this field belongs to. */ private final ClassModel classModel; private final Property property; /** * Customization of this property. */ private final PropertyCustomization customization; private final MethodHandle getValueHandle; private final MethodHandle setValueHandle; private final Field field; private final Method getter; private final Method setter; private final Type getterMethodType; private final Type setterMethodType; /** * Create a new PropertyModel that merges two existing PropertyModel that have identical read/write names. * The input PropertyModel objects MUST be equal (a.equals(b) == true) * * @param a a PropertyModel instance to merge * @param b the other PropertyModel instance to merge */ public PropertyModel(PropertyModel a, PropertyModel b) { if (!a.equals(b)) { throw new IllegalStateException("Property models " + a + " and " + b + " cannot be merged"); } // Initial cloning steps this.classModel = a.classModel; this.propertyName = a.propertyName; this.readName = a.readName; this.writeName = a.writeName; this.propertyType = a.propertyType; this.customization = a.customization; // Merging steps this.getterMethodType = a.getterMethodType != null ? a.getterMethodType : b.getterMethodType; this.setterMethodType = a.setterMethodType != null ? a.setterMethodType : b.setterMethodType; this.property = a.property; if (b.property.getField() != null) { this.property.setField(b.property.getField()); } if (b.property.getGetter() != null) { this.property.setGetter(b.property.getGetter()); } if (b.property.getSetter() != null) { this.property.setSetter(b.property.getSetter()); } this.field = property.getField(); this.getter = property.getGetter(); this.setter = property.getSetter(); PropertyVisibilityStrategy strategy = classModel.getClassCustomization().getPropertyVisibilityStrategy(); this.getValueHandle = createReadHandle(field, getter, isMethodVisible(getter, strategy), strategy); this.setValueHandle = createWriteHandle(field, setter, isMethodVisible(setter, strategy), strategy); } /** * Creates an instance. * * @param classModel Class model of declaring class. * @param property Property. * @param jsonbContext Context. */ public PropertyModel(ClassModel classModel, Property property, JsonbContext jsonbContext) { this.classModel = classModel; this.property = property; this.propertyName = property.getName(); this.propertyType = property.getPropertyType(); this.field = property.getField(); this.getter = property.getGetter(); this.setter = property.getSetter(); PropertyVisibilityStrategy strategy = classModel.getClassCustomization().getPropertyVisibilityStrategy(); boolean getterVisible = isMethodVisible(getter, strategy); boolean setterVisible = isMethodVisible(setter, strategy); this.getValueHandle = createReadHandle(field, getter, getterVisible, strategy); this.setValueHandle = createWriteHandle(field, setter, setterVisible, strategy); this.getterMethodType = getterVisible ? property.getGetterType() : null; this.setterMethodType = setterVisible ? property.getSetterType() : null; this.customization = introspectCustomization(property, jsonbContext, classModel); this.readName = calculateReadWriteName(customization.getJsonReadName(), propertyName, jsonbContext.getConfigProperties().getPropertyNamingStrategy()); this.writeName = calculateReadWriteName(customization.getJsonWriteName(), propertyName, jsonbContext.getConfigProperties().getPropertyNamingStrategy()); } /** * Returns which type should be used to deserialization. * * @return deserialization type */ public Type getPropertyDeserializationType() { return setterMethodType == null ? propertyType : setterMethodType; } /** * Returns which type should be used to serialization. * * @return serialization type */ public Type getPropertySerializationType() { return getterMethodType == null ? propertyType : getterMethodType; } private SerializerBinding getUserSerializerBinding(Property property, JsonbContext jsonbContext) { final SerializerBinding serializerBinding = jsonbContext.getAnnotationIntrospector().getSerializerBinding(property); if (serializerBinding != null) { return serializerBinding; } return jsonbContext.getComponentMatcher().getSerializerBinding(getPropertySerializationType(), null).orElse(null); } private PropertyCustomization introspectCustomization(Property property, JsonbContext jsonbContext, ClassModel classModel) { final AnnotationIntrospector introspector = jsonbContext.getAnnotationIntrospector(); final PropertyCustomization.Builder builder = PropertyCustomization.builder(); //drop all other annotations for transient properties EnumSet transientInfo = introspector.getJsonbTransientCategorized(property); ClassModel parent = classModel; // Check parent classes for transient annotations while ((parent = parent.getParentClassModel()) != null) { PropertyModel parentProperty = parent.getPropertyModel(property.getName()); if (parentProperty != null) { if (parentProperty.customization.isReadTransient()) { transientInfo.add(AnnotationTarget.GETTER); } if (parentProperty.customization.isWriteTransient()) { transientInfo.add(AnnotationTarget.SETTER); } } } if (transientInfo.size() != 0) { builder.readTransient(transientInfo.contains(AnnotationTarget.GETTER)); builder.writeTransient(transientInfo.contains(AnnotationTarget.SETTER)); if (transientInfo.contains(AnnotationTarget.PROPERTY)) { if (!transientInfo.contains(AnnotationTarget.GETTER)) { builder.readTransient(true); } if (!transientInfo.contains(AnnotationTarget.SETTER)) { builder.writeTransient(true); } } if (builder.readTransient()) { introspector.checkTransientIncompatible(property.getFieldElement()); introspector.checkTransientIncompatible(property.getGetterElement()); } if (builder.writeTransient()) { introspector.checkTransientIncompatible(property.getFieldElement()); introspector.checkTransientIncompatible(property.getSetterElement()); } } if (!builder.readTransient()) { builder.jsonWriteName(introspector.getJsonbPropertyJsonWriteName(property)); builder.nillable(introspector.isPropertyNillable(property).orElse(classModel.getClassCustomization().isNillable())); builder.serializerBinding(getUserSerializerBinding(property, jsonbContext)); } if (!builder.writeTransient()) { builder.jsonReadName(introspector.getJsonbPropertyJsonReadName(property)); builder.deserializerBinding(introspector.getDeserializerBinding(property)); } final AdapterBinding adapterBinding = jsonbContext.getAnnotationIntrospector().getAdapterBinding(property); if (adapterBinding != null) { builder.serializeAdapter(adapterBinding); builder.deserializeAdapter(adapterBinding); } else { builder.serializeAdapter(jsonbContext.getComponentMatcher() .getSerializeAdapterBinding(getPropertySerializationType(), null).orElse(null)); builder.deserializeAdapter(jsonbContext.getComponentMatcher() .getDeserializeAdapterBinding(getPropertyDeserializationType(), null) .orElse(null)); } introspectDateFormatter(property, introspector, builder, jsonbContext); introspectNumberFormatter(property, introspector, builder); builder.implementationClass(introspector.getImplementationClass(property)); return builder.build(); } private static void introspectDateFormatter(Property property, AnnotationIntrospector introspector, PropertyCustomization.Builder builder, JsonbContext jsonbContext) { /* * If @JsonbDateFormat is placed on getter implementation must use this format on serialization. * If @JsonbDateFormat is placed on setter implementation must use this format on deserialization. * If @JsonbDateFormat is placed on field implementation must use this format on serialization and deserialization. * * Priority from high to low is getter / setter > field > class > package > global configuration */ Map jsonDateFormatCategorized = introspector .getJsonbDateFormatCategorized(property); final JsonbDateFormatter configDateFormatter = jsonbContext.getConfigProperties().getConfigDateFormatter(); if (!builder.readTransient()) { final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, AnnotationTarget.GETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS); builder.serializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter); } if (!builder.writeTransient()) { final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized, AnnotationTarget.SETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS); builder.deserializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter); } } private static void introspectNumberFormatter(Property property, AnnotationIntrospector introspector, PropertyCustomization.Builder builder) { /* * If @JsonbNumberFormat is placed on getter implementation must use this format on serialization. * If @JsonbNumberFormat is placed on setter implementation must use this format on deserialization. * If @JsonbNumberFormat is placed on field implementation must use this format on serialization and deserialization. * * Priority from high to low is getter / setter > field > class > package > global configuration */ Map jsonNumberFormatCategorized = introspector.getJsonNumberFormatter(property); if (!builder.readTransient()) { builder.serializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized, AnnotationTarget.GETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)); } if (!builder.writeTransient()) { builder.deserializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized, AnnotationTarget.SETTER, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)); } } /** * Pull result for most significant scope defined by order of annotation targets. * * @param collectedAnnotations all targets * @param targets ordered target types by scope */ private static T getTargetForMostPreciseScope(Map collectedAnnotations, AnnotationTarget... targets) { for (AnnotationTarget target : targets) { final T result = collectedAnnotations.get(target); if (result != null) { return result; } } return null; } /** * Gets property's value. * * @param object object to read property from * @return property's value */ public Object getValue(Object object) { try { return getValueHandle.invoke(object); } catch (Throwable e) { throw new JsonbException("Error getting value on: " + object, e); } } /** * Sets a property. * * If not writable (final, transient, static), ignores property. * * @param object Object to set value in. * @param value Value to set. */ public void setValue(Object object, Object value) { if (!isWritable()) { return; } try { setValueHandle.invoke(object, value); } catch (Throwable e) { throw new JsonbException("Error setting value on: " + object, e); } } /** * Property is readable. Based on access policy and java field modifiers. * * @return true if can be serialized to JSON */ public boolean isReadable() { return !customization.isReadTransient() && this.getValueHandle != null; } /** * Property is writable. Based on access policy and java field modifiers. * * @return true if can be deserialized from JSON */ public boolean isWritable() { return !customization.isWriteTransient() && this.setValueHandle != null; } /** * Default property name according to Field / Getter / Setter method names. * This name is use for identifying properties, for JSON serialization is used customized name * which may be derived from default name. * * @return default name */ public String getPropertyName() { return propertyName; } /** * Model of declaring class of this property. * * @return class model */ public ClassModel getClassModel() { return classModel; } /** * Introspected customization of a property. * * @return immutable property customization */ public PropertyCustomization getCustomization() { return customization; } @Override public int compareTo(PropertyModel o) { int compare = readName.compareTo(o.readName); return compare == 0 ? writeName.compareTo(o.writeName) : compare; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PropertyModel other = (PropertyModel) o; return Objects.equals(readName, other.readName) && Objects.equals(writeName, other.writeName); } @Override public int hashCode() { return Objects.hash(readName, writeName); } /** * Gets a name of JSON document property to read this property from. * * @return Name of JSON document property. */ public String getReadName() { return readName; } public String getWriteName() { return writeName; } /** * If customized by JsonbPropertyAnnotation, than is used, otherwise use strategy to translate. * Since this is cached for performance reasons strategy has to be consistent * with calculated values for same input. */ private static String calculateReadWriteName(String readWriteName, String propertyName, PropertyNamingStrategy strategy) { return readWriteName != null ? readWriteName : strategy.translateName(propertyName); } /** * Field of a javabean property. * * @return {@link Field field} */ public Field getField() { return field; } /** * Setter of a javabean property. * * @return {@link Method getter} */ public Method getGetter() { return getter; } /** * Getter of a javabean property. * * @return {@link Method setter} */ public Method getSetter() { return setter; } // Used in ClassParser public static boolean isPropertyReadable(Field field, Method getter, PropertyVisibilityStrategy strategy) { return createReadHandle(field, getter, isMethodVisible(getter, strategy), strategy) != null; } private static MethodHandle createReadHandle(Field field, Method getter, boolean getterVisible, PropertyVisibilityStrategy strategy) { boolean fieldReadable = field == null || (field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0; if (fieldReadable) { if (getter != null && getterVisible) { try { return LOOKUP.unreflect(getter); } catch (Throwable e) { throw new JsonbException("Error accessing getter '" + getter.getName() + "' declared in '" + getter .getDeclaringClass() + "'", e); } } if (isFieldVisible(field, getter, strategy)) { try { return LOOKUP.unreflectGetter(field); } catch (IllegalAccessException e) { throw new JsonbException("Error accessing field '" + field.getName() + "' declared in '" + field .getDeclaringClass() + "'", e); } } } return null; } private static MethodHandle createWriteHandle(Field field, Method setter, boolean setterVisible, PropertyVisibilityStrategy strategy) { boolean fieldWritable = field == null || (field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC | Modifier.FINAL)) == 0; if (fieldWritable) { if (setter != null && setterVisible && !setter.getDeclaringClass().isAnonymousClass()) { try { return LOOKUP.unreflect(setter); } catch (IllegalAccessException e) { throw new JsonbException("Error accessing setter '" + setter.getName() + "' declared in '" + setter .getDeclaringClass() + "'", e); } } if (isFieldVisible(field, setter, strategy) && !field.getDeclaringClass().isAnonymousClass()) { try { return LOOKUP.unreflectSetter(field); } catch (IllegalAccessException e) { throw new JsonbException("Error accessing field '" + field.getName() + "' declared in '" + field .getDeclaringClass() + "'", e); } } } return null; } private static boolean isFieldVisible(Field field, Method method, PropertyVisibilityStrategy strategy) { if (field == null) { return false; } boolean accessible = isVisible(strat -> strat.isVisible(field), method, strategy); //overridden by strategy, or anonymous class (readable by spec) if (accessible && ( !Modifier.isPublic(field.getModifiers()) || field.getDeclaringClass().isAnonymousClass() || isNotPublicAndNonNested(field.getDeclaringClass()))) { overrideAccessible(field); } return accessible; } private static boolean isNotPublicAndNonNested(Class declaringClass) { return !declaringClass.isMemberClass() && !Modifier.isPublic(declaringClass.getModifiers()); } private static boolean isMethodVisible(Method method, PropertyVisibilityStrategy strategy) { if (method == null || Modifier.isStatic(method.getModifiers())) { return false; } boolean accessible = isVisible(strat -> strat.isVisible(method), method, strategy); //overridden by strategy, anonymous class, or lambda if (accessible && ( !Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isAnonymousClass() || method .getDeclaringClass().isSynthetic())) { overrideAccessible(method); } return accessible; } private static void overrideAccessible(AccessibleObject accessibleObject) { AccessController.doPrivileged((PrivilegedAction) () -> { accessibleObject.setAccessible(true); return null; }); } /** * Look up class and package level @JsonbVisibility, or global config PropertyVisibilityStrategy. * If any is found it is used for resolving visibility by calling provided visibilityCheckFunction. * * @param visibilityCheckFunction function declaring visibility check * @return Optional with result of visibility check, or empty optional if no strategy is found */ private static boolean isVisible(Predicate visibilityCheckFunction, Method method, PropertyVisibilityStrategy strategy) { return strategy != null ? visibilityCheckFunction.test(strategy) : visibilityCheckFunction.test(new DefaultVisibilityStrategy(method)); } private static final class DefaultVisibilityStrategy implements PropertyVisibilityStrategy { private final Method method; DefaultVisibilityStrategy(Method method) { this.method = method; } @Override public boolean isVisible(Field field) { //don't check field if getter is not visible (forced by spec) return (method == null || isVisible(method)) && Modifier.isPublic(field.getModifiers()); } @Override public boolean isVisible(Method method) { return Modifier.isPublic(method.getModifiers()); } } public MethodHandle getGetValueHandle() { return getValueHandle; } public MethodHandle getSetValueHandle() { return setValueHandle; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.util.Comparator; import java.util.TreeMap; /** * TreeMap with a reverse ordering by default. * * @param comparable key * @param value */ public class ReverseTreeMap, V> extends TreeMap { /** * Default constructor of a TreeMap with reverse order. */ public ReverseTreeMap() { super(Comparator.reverseOrder()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/ClassCustomization.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import jakarta.json.bind.config.PropertyVisibilityStrategy; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.model.JsonbCreator; /** * Customization which could be applied on a class or package level. */ public class ClassCustomization extends CustomizationBase { private static final ClassCustomization EMPTY = new ClassCustomization(new Builder()); private final JsonbCreator creator; private final String[] propertyOrder; private final JsonbNumberFormatter numberFormatter; private final JsonbDateFormatter dateTimeFormatter; private final PropertyVisibilityStrategy propertyVisibilityStrategy; private final TypeInheritanceConfiguration typeInheritanceConfiguration; /** * Copies properties from builder an creates immutable instance. * * @param builder not null */ private ClassCustomization(Builder builder) { super(builder); this.creator = builder.creator; this.propertyOrder = builder.propertyOrder; this.numberFormatter = builder.numberFormatter; this.dateTimeFormatter = builder.dateTimeFormatter; this.propertyVisibilityStrategy = builder.propertyVisibilityStrategy; this.typeInheritanceConfiguration = builder.typeInheritanceConfiguration; } public static ClassCustomization empty() { return EMPTY; } public static Builder builder() { return new Builder(); } /** * Returns instance of {@link JsonbCreator}. * * @return instance of creator */ public JsonbCreator getCreator() { return creator; } /** * Names of properties to sort with. * * @return sorted names of properties */ public String[] getPropertyOrder() { return propertyOrder; } /** * Property visibility strategy for this class model. * * @return visibility strategy */ public PropertyVisibilityStrategy getPropertyVisibilityStrategy() { return propertyVisibilityStrategy; } @Override public JsonbNumberFormatter getSerializeNumberFormatter() { return numberFormatter; } @Override public JsonbNumberFormatter getDeserializeNumberFormatter() { return numberFormatter; } @Override public JsonbDateFormatter getSerializeDateFormatter() { return dateTimeFormatter; } @Override public JsonbDateFormatter getDeserializeDateFormatter() { return dateTimeFormatter; } public TypeInheritanceConfiguration getPolymorphismConfig() { return typeInheritanceConfiguration; } /** * The customization builder that would be used to build an instance of {@link ClassCustomization} to ensure its immutability. */ public static class Builder extends CustomizationBase.Builder { private JsonbCreator creator; private String[] propertyOrder; private JsonbNumberFormatter numberFormatter; private JsonbDateFormatter dateTimeFormatter; private PropertyVisibilityStrategy propertyVisibilityStrategy; private TypeInheritanceConfiguration typeInheritanceConfiguration; private Builder() { } @Override public Builder of(ClassCustomization customization) { super.of(customization); creator(customization.creator); propertyOrder(customization.propertyOrder); numberFormatter(customization.numberFormatter); dateTimeFormatter(customization.dateTimeFormatter); propertyVisibilityStrategy(customization.propertyVisibilityStrategy); return this; } public Builder creator(JsonbCreator creator) { this.creator = creator; return this; } public Builder propertyOrder(String[] propertyOrder) { this.propertyOrder = propertyOrder; return this; } public Builder numberFormatter(JsonbNumberFormatter numberFormatter) { this.numberFormatter = numberFormatter; return this; } public Builder dateTimeFormatter(JsonbDateFormatter dateTimeFormatter) { this.dateTimeFormatter = dateTimeFormatter; return this; } public Builder propertyVisibilityStrategy(PropertyVisibilityStrategy propertyVisibilityStrategy) { this.propertyVisibilityStrategy = propertyVisibilityStrategy; return this; } public Builder polymorphismConfig(TypeInheritanceConfiguration typeInheritanceConfiguration) { this.typeInheritanceConfiguration = typeInheritanceConfiguration; return this; } @Override public ClassCustomization build() { return new ClassCustomization(this); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.components.SerializerBinding; /** * Customization which is aware of bound components, such as adapters and (de)serializers. */ public interface ComponentBoundCustomization { /** * @return Adapter wrapper class with resolved generic information. */ AdapterBinding getSerializeAdapterBinding(); /** * @return Adapter wrapper class with resolved generic information. */ AdapterBinding getDeserializeAdapterBinding(); /** * Serializer wrapper with resolved generic info. * * @return serializer wrapper */ SerializerBinding getSerializerBinding(); /** * Deserializer wrapper with resolved generic info. * * @return deserializer wrapper */ DeserializerBinding getDeserializerBinding(); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/CreatorCustomization.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.model.PropertyModel; /** * Customization for creator (constructor / factory methods) parameters. */ public class CreatorCustomization extends CustomizationBase { private final JsonbNumberFormatter numberFormatter; private final JsonbDateFormatter dateFormatter; private final boolean required; private PropertyModel propertyModel; /** * Creates new creator customization instance. * * @param builder builder of the customization */ private CreatorCustomization(Builder builder) { super(builder); this.numberFormatter = builder.numberFormatter; this.dateFormatter = builder.dateFormatter; this.required = builder.required; } public static Builder builder() { return new Builder(); } @Override public JsonbNumberFormatter getSerializeNumberFormatter() { throw new UnsupportedOperationException("Serialization is not supported for creator parameters."); } @Override public JsonbNumberFormatter getDeserializeNumberFormatter() { if (numberFormatter != null) { return numberFormatter; } else if (propertyModel != null) { return propertyModel.getCustomization().getDeserializeNumberFormatter(); } return null; } @Override public JsonbDateFormatter getSerializeDateFormatter() { throw new UnsupportedOperationException("Serialization is not supported for creator parameters."); } @Override public JsonbDateFormatter getDeserializeDateFormatter() { if (dateFormatter != null) { return dateFormatter; } else if (propertyModel != null) { return propertyModel.getCustomization().getDeserializeDateFormatter(); } return null; } @Override public boolean isNillable() { throw new UnsupportedOperationException("Not supported for creator parameters."); } /** * Set property referenced model. * * @param propertyModel referenced property model */ public void setPropertyModel(PropertyModel propertyModel) { this.propertyModel = propertyModel; } public boolean isRequired() { return required; } public static final class Builder extends CustomizationBase.Builder { private JsonbNumberFormatter numberFormatter; private JsonbDateFormatter dateFormatter; private boolean required = false; private Builder() { } @Override public Builder of(CreatorCustomization customization) { super.of(customization); numberFormatter = customization.numberFormatter; dateFormatter = customization.dateFormatter; return this; } public Builder numberFormatter(JsonbNumberFormatter numberFormatter) { this.numberFormatter = numberFormatter; return this; } public Builder dateFormatter(JsonbDateFormatter dateFormatter) { this.dateFormatter = dateFormatter; return this; } public Builder required(boolean required) { this.required = required; return this; } @Override public CreatorCustomization build() { return new CreatorCustomization(this); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/Customization.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; /** * Customization configuration for class or field. * Configuration parsed from annotation is put here. * Immutable. */ public interface Customization { /** * Number formatter for formatting numbers during serialization process. It could be the same formatter instance used for * deserialization * (returned by {@link #getDeserializeNumberFormatter()} * * @return number formatter */ JsonbNumberFormatter getSerializeNumberFormatter(); /** * Number formatter for formatting numbers during deserialization process. It could be the same formatter instance used for * serialization * (returned by {@link #getSerializeNumberFormatter()} * * @return number formatter */ JsonbNumberFormatter getDeserializeNumberFormatter(); /** * Date formatter for formatting date values during serialization process. It could be the same formatter instance used for * deserialization * (returned by {@link #getDeserializeDateFormatter()}. If not set, defaulted to jakarta.json.bind.annotation * .JsonbDateFormat.DEFAULT_FORMAT. * * * @return date formatter */ JsonbDateFormatter getSerializeDateFormatter(); /** * Date formatter for formatting date values during deserialization process. It could be the same formatter instance used * for serialization * (returned by {@link #getSerializeDateFormatter()}. If not set, defaulted to jakarta.json.bind.annotation * .JsonbDateFormat.DEFAULT_FORMAT. * * * @return date formatter */ JsonbDateFormatter getDeserializeDateFormatter(); /** * Returns true if nillable customization is present. * * @return True if nillable customization is present. */ boolean isNillable(); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.DeserializerBinding; import org.eclipse.yasson.internal.components.SerializerBinding; /** * Common properties of {@link ClassCustomization} and {@link PropertyCustomization}. */ abstract class CustomizationBase implements Customization, ComponentBoundCustomization { private final AdapterBinding adapterBinding; private final SerializerBinding serializerBinding; private final DeserializerBinding deserializerBinding; private final boolean nillable; /** * Copies properties from builder an creates immutable instance. * * @param builder not null */ CustomizationBase(Builder builder) { this.nillable = builder.nillable; this.adapterBinding = builder.adapterBinding; this.serializerBinding = builder.serializerBinding; this.deserializerBinding = builder.deserializerBinding; } /** * Returns true if nillable customization is present. * * @return True if nillable customization is present. */ public boolean isNillable() { return nillable; } public AdapterBinding getSerializeAdapterBinding() { return adapterBinding; } @Override public AdapterBinding getDeserializeAdapterBinding() { return adapterBinding; } /** * Serializer wrapper with resolved generic info. * * @return serializer wrapper */ public SerializerBinding getSerializerBinding() { return serializerBinding; } /** * Deserializer wrapper with resolved generic info. * * @return deserializer wrapper */ public DeserializerBinding getDeserializerBinding() { return deserializerBinding; } @SuppressWarnings("unchecked") abstract static class Builder, B extends CustomizationBase> { private AdapterBinding adapterBinding; private SerializerBinding serializerBinding; private DeserializerBinding deserializerBinding; private boolean nillable; Builder() { } public T of(B customization) { adapterBinding = customization.getDeserializeAdapterBinding(); serializerBinding = customization.getSerializerBinding(); deserializerBinding = customization.getDeserializerBinding(); nillable = customization.isNillable(); return (T) this; } public T adapterBinding(AdapterBinding adapterBinding) { this.adapterBinding = adapterBinding; return (T) this; } public T serializerBinding(SerializerBinding serializerBinding) { this.serializerBinding = serializerBinding; return (T) this; } public T deserializerBinding(DeserializerBinding deserializerBinding) { this.deserializerBinding = deserializerBinding; return (T) this; } public T nillable(boolean nillable) { this.nillable = nillable; return (T) this; } public abstract B build(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.components.AdapterBinding; /** * Customization for a property of a class. */ public class PropertyCustomization extends CustomizationBase { private final String jsonReadName; private final String jsonWriteName; private final JsonbNumberFormatter serializeNumberFormatter; private final JsonbNumberFormatter deserializeNumberFormatter; private final JsonbDateFormatter serializeDateFormatter; private final JsonbDateFormatter deserializeDateFormatter; private final AdapterBinding serializeAdapter; private final AdapterBinding deserializeAdapter; private final boolean readTransient; private final boolean writeTransient; private final Class implementationClass; /** * Copies properties from builder an creates immutable instance. * * @param builder not null */ private PropertyCustomization(Builder builder) { super(builder); this.serializeAdapter = builder.serializeAdapter; this.deserializeAdapter = builder.deserializeAdapter; this.jsonReadName = builder.jsonReadName; this.jsonWriteName = builder.jsonWriteName; this.serializeNumberFormatter = builder.serializeNumberFormatter; this.deserializeNumberFormatter = builder.deserializeNumberFormatter; this.serializeDateFormatter = builder.serializeDateFormatter; this.deserializeDateFormatter = builder.deserializeDateFormatter; this.readTransient = builder.readTransient; this.writeTransient = builder.writeTransient; this.implementationClass = builder.implementationClass; } public static Builder builder() { return new Builder(); } /** * Name if specified for property setter with {@link jakarta.json.bind.annotation.JsonbProperty}. * * @return read name */ public String getJsonReadName() { return jsonReadName; } /** * Name if specified for property getter with {@link jakarta.json.bind.annotation.JsonbProperty}. * * @return write name */ public String getJsonWriteName() { return jsonWriteName; } @Override public JsonbNumberFormatter getSerializeNumberFormatter() { return serializeNumberFormatter; } @Override public JsonbNumberFormatter getDeserializeNumberFormatter() { return deserializeNumberFormatter; } @Override public JsonbDateFormatter getSerializeDateFormatter() { return serializeDateFormatter; } @Override public JsonbDateFormatter getDeserializeDateFormatter() { return deserializeDateFormatter; } /** * The flag indicating whether the value of the underlying type/property should be processed during serialization process * or not. * * @return true indicates that the underlying type/property should be included in serialization process and false indicates * it should not */ public boolean isReadTransient() { return readTransient; } /** * The flag indicating whether the value of the underlying type/property should be processed during deserialization process * or not. * * @return true indicates that the underlying type/property should be included in deserialization process and false * indicates it should not */ public boolean isWriteTransient() { return writeTransient; } /** * Implementation class if property is interface type. * * @return class implementing property interface */ public Class getImplementationClass() { return implementationClass; } @Override public AdapterBinding getDeserializeAdapterBinding() { return deserializeAdapter; } @Override public AdapterBinding getSerializeAdapterBinding() { return serializeAdapter; } public static final class Builder extends CustomizationBase.Builder { private String jsonReadName; private String jsonWriteName; private JsonbNumberFormatter serializeNumberFormatter; private JsonbNumberFormatter deserializeNumberFormatter; private JsonbDateFormatter serializeDateFormatter; private JsonbDateFormatter deserializeDateFormatter; private AdapterBinding serializeAdapter; private AdapterBinding deserializeAdapter; private boolean readTransient; private boolean writeTransient; private Class implementationClass; private Builder() { } @Override public Builder of(PropertyCustomization customization) { jsonReadName = customization.jsonReadName; jsonWriteName = customization.jsonWriteName; serializeNumberFormatter = customization.serializeNumberFormatter; deserializeNumberFormatter = customization.deserializeNumberFormatter; serializeDateFormatter = customization.serializeDateFormatter; deserializeDateFormatter = customization.deserializeDateFormatter; serializeAdapter = customization.serializeAdapter; deserializeAdapter = customization.deserializeAdapter; readTransient = customization.readTransient; writeTransient = customization.writeTransient; implementationClass = customization.implementationClass; return super.of(customization); } /** * Set a JSON property name used to read a property value from on deserialization. * * @param jsonReadName JSON property name */ public Builder jsonReadName(String jsonReadName) { this.jsonReadName = jsonReadName; return this; } /** * Set a property name which is written to JSON document on serialization. * * @param jsonWriteName Property name. */ public Builder jsonWriteName(String jsonWriteName) { this.jsonWriteName = jsonWriteName; return this; } /** * Set number formatter for formatting numbers during serialization process. * * @param serializeNumberFormatter Number formatter for formatting numbers during serialization process. */ public Builder serializeNumberFormatter(JsonbNumberFormatter serializeNumberFormatter) { this.serializeNumberFormatter = serializeNumberFormatter; return this; } /** * Set number formatter for formatting numbers during deserialization process. * * @param deserializeNumberFormatter Number formatter for formatting numbers during deserialization process. */ public Builder deserializeNumberFormatter(JsonbNumberFormatter deserializeNumberFormatter) { this.deserializeNumberFormatter = deserializeNumberFormatter; return this; } /** * Set date formatter for formatting dates during serialization process. * * @param serializeDateFormatter Date formatter for formatting dates during serialization process. */ public Builder serializeDateFormatter(JsonbDateFormatter serializeDateFormatter) { this.serializeDateFormatter = serializeDateFormatter; return this; } /** * Set date formatter for formatting dates during deserialization process. * * @param deserializeDateFormatter Date formatter for formatting dates during deserialization process. */ public Builder deserializeDateFormatter(JsonbDateFormatter deserializeDateFormatter) { this.deserializeDateFormatter = deserializeDateFormatter; return this; } public Builder serializeAdapter(AdapterBinding serializeAdapter) { this.serializeAdapter = serializeAdapter; return this; } public Builder deserializeAdapter(AdapterBinding deserializeAdapter) { this.deserializeAdapter = deserializeAdapter; return this; } /** * Sets a presence of read transient customization. * * @param readTransient Presence of read transient customization. */ public Builder readTransient(boolean readTransient) { this.readTransient = readTransient; return this; } public boolean readTransient() { return readTransient; } /** * Sets a presence of write transient customization. * * @param writeTransient Presence of write transient customization. */ public Builder writeTransient(boolean writeTransient) { this.writeTransient = writeTransient; return this; } public boolean writeTransient() { return writeTransient; } /** * Implementation class if property is interface type. * * @param implementationClass implementing property interface */ public Builder implementationClass(Class implementationClass) { this.implementationClass = implementationClass; return this; } @Override public PropertyCustomization build() { return new PropertyCustomization(this); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/PropertyOrdering.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyOrderStrategy; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.PropertyModel; /** * Order properties in bean object. {@link jakarta.json.bind.annotation.JsonbPropertyOrder} have always precedence. * If configured with {@link JsonbConfig} provided property order strategy will be used. */ public class PropertyOrdering { private final Consumer> propertyOrderStrategy; /** * Creates a new instance. * * @param propertyOrderStrategy Property order strategy. Must be not null. */ public PropertyOrdering(Consumer> propertyOrderStrategy) { this.propertyOrderStrategy = Objects.requireNonNull(propertyOrderStrategy); } /** * Sorts class properties either, by class {@link jakarta.json.bind.annotation.JsonbPropertyOrder} annotation, * or by {@link PropertyOrderStrategy} if set in {@link JsonbConfig}. * * @param properties Properties to sort. * @param classModel Class model. * @return Sorted list of properties. */ public List orderProperties(List properties, ClassModel classModel) { Map byReadName = new HashMap<>(); properties.forEach(propertyModel -> byReadName.put(propertyModel.getPropertyName(), propertyModel)); String[] order = classModel.getClassCustomization().getPropertyOrder(); List sortedProperties = new ArrayList<>(); if (order != null) { //if @JsonbPropertyOrder annotation is defined on a class for (String propName : order) { final PropertyModel remove = byReadName.remove(propName); if (remove != null) { sortedProperties.add(remove); } } } List readNamesToSort = new ArrayList<>(byReadName.values()); propertyOrderStrategy.accept(readNamesToSort); sortedProperties.addAll(readNamesToSort); return sortedProperties; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import java.nio.CharBuffer; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyNamingStrategy; import org.eclipse.yasson.internal.model.PropertyModel; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import static java.util.Comparator.comparing; import static jakarta.json.bind.config.PropertyNamingStrategy.CASE_INSENSITIVE; import static jakarta.json.bind.config.PropertyNamingStrategy.IDENTITY; import static jakarta.json.bind.config.PropertyNamingStrategy.LOWER_CASE_WITH_DASHES; import static jakarta.json.bind.config.PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES; import static jakarta.json.bind.config.PropertyNamingStrategy.UPPER_CAMEL_CASE; import static jakarta.json.bind.config.PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES; import static jakarta.json.bind.config.PropertyOrderStrategy.ANY; import static jakarta.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL; import static jakarta.json.bind.config.PropertyOrderStrategy.REVERSE; /** * Provides strategies for {@link jakarta.json.bind.config.PropertyNamingStrategy} and * {@link jakarta.json.bind.config.PropertyOrderStrategy}. */ public final class StrategiesProvider { private StrategiesProvider() { } /** * Case insensitive naming strategy. */ public static final PropertyNamingStrategy CASE_INSENSITIVE_STRATEGY = Objects::requireNonNull; /** * Returns an ordering strategy which corresponds to the ordering strategy name. * * @param strategy ordering strategy name * @return ordering strategy */ public static Consumer> getOrderingFunction(String strategy) { switch (strategy) { case LEXICOGRAPHICAL: return props -> props.sort(comparing(PropertyModel::getWriteName)); case ANY: return props -> { }; case REVERSE: return props -> props.sort(comparing(PropertyModel::getWriteName).reversed()); default: throw new JsonbException(Messages.getMessage(MessageKeys.PROPERTY_ORDER, strategy)); } } /** * Returns a naming strategy which corresponds to the naming strategy name. * * @param strategy naming strategy name * @return naming strategy */ public static PropertyNamingStrategy getPropertyNamingStrategy(String strategy) { switch (strategy) { case LOWER_CASE_WITH_UNDERSCORES: return createLowerCaseStrategyWithSeparator('_'); case LOWER_CASE_WITH_DASHES: return createLowerCaseStrategyWithSeparator('-'); case UPPER_CAMEL_CASE: return createUpperCamelCaseStrategy(); case UPPER_CAMEL_CASE_WITH_SPACES: return createUpperCamelCaseWithSpaceStrategy(); case IDENTITY: return Objects::requireNonNull; case CASE_INSENSITIVE: return CASE_INSENSITIVE_STRATEGY; default: throw new JsonbException("No property naming strategy was found for: " + strategy); } } private static PropertyNamingStrategy createUpperCamelCaseStrategy() { return propertyName -> { Objects.requireNonNull(propertyName); char first = Character.toUpperCase(propertyName.charAt(0)); return first + propertyName.substring(1); }; } private static PropertyNamingStrategy createUpperCamelCaseWithSpaceStrategy() { return propertyName -> { String upperCased = createUpperCamelCaseStrategy().translateName(propertyName); CharBuffer buffer = CharBuffer.allocate(upperCased.length() * 2); char last = Character.MIN_VALUE; for (int i = 0; i < upperCased.length(); ++i) { char current = upperCased.charAt(i); if (i > 0 && Character.isUpperCase(current) && isLowerCaseCharacter(last)) { buffer.append(' '); } last = current; buffer.append(current); } return new String(buffer.array(), 0, buffer.position()); }; } private static PropertyNamingStrategy createLowerCaseStrategyWithSeparator(char separator) { return propertyName -> { Objects.requireNonNull(propertyName); CharBuffer charBuffer = CharBuffer.allocate(propertyName.length() * 2); char last = Character.MIN_VALUE; for (int i = 0; i < propertyName.length(); ++i) { char current = propertyName.charAt(i); if (i > 0 && Character.isUpperCase(current) && isLowerCaseCharacter(last)) { charBuffer.append(separator); } last = current; charBuffer.append(Character.toLowerCase(current)); } return new String(charBuffer.array(), 0, charBuffer.position()); }; } private static boolean isLowerCaseCharacter(char character) { return Character.isAlphabetic(character) && Character.isLowerCase(character); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/TypeInheritanceConfiguration.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import java.util.HashMap; import java.util.Map; import java.util.Objects; import jakarta.json.bind.annotation.JsonbTypeInfo; /** * Type inheritance configuration. */ public class TypeInheritanceConfiguration { private final String fieldName; private final boolean inherited; private final Map, String> aliases; private final Class definedType; private final TypeInheritanceConfiguration parentConfig; private TypeInheritanceConfiguration(Builder builder) { this.fieldName = builder.fieldName; this.inherited = builder.inherited; this.aliases = Map.copyOf(builder.aliases); this.parentConfig = builder.parentConfig; this.definedType = builder.definedType; } public static Builder builder() { return new Builder(); } public String getFieldName() { return fieldName; } public boolean isInherited() { return inherited; } public Map, String> getAliases() { return aliases; } public Class getDefinedType() { return definedType; } public TypeInheritanceConfiguration getParentConfig() { return parentConfig; } public static final class Builder { private Map, String> aliases = new HashMap<>(); private String fieldName = JsonbTypeInfo.DEFAULT_KEY_NAME; private boolean inherited = false; private Class definedType; private TypeInheritanceConfiguration parentConfig; private Builder() { } public Builder inherited(boolean inherited) { this.inherited = inherited; return this; } public Builder fieldName(String fieldName) { this.fieldName = Objects.requireNonNull(fieldName); return this; } public Builder alias(Class clazz, String alias) { this.aliases.put(clazz, alias); return this; } public Builder parentConfig(TypeInheritanceConfiguration parentConfig) { this.parentConfig = parentConfig; return this; } public Builder definedType(Class definedType) { this.definedType = definedType; return this; } public Builder of(TypeInheritanceConfiguration typeInheritanceConfiguration) { this.fieldName = typeInheritanceConfiguration.fieldName; this.aliases = new HashMap<>(typeInheritanceConfiguration.aliases); this.inherited = typeInheritanceConfiguration.inherited; this.parentConfig = typeInheritanceConfiguration.parentConfig; this.definedType = typeInheritanceConfiguration.definedType; return this; } public TypeInheritanceConfiguration build() { return new TypeInheritanceConfiguration(this); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/model/customization/VisibilityStrategiesProvider.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyVisibilityStrategy; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Provider of the JSON-B visibility strategies. */ public class VisibilityStrategiesProvider { private static final PropertyVisibilityStrategy PUBLIC_PROPERTY = new PublicPropertyVisibilityStrategy(); private static final PropertyVisibilityStrategy PUBLIC_ACCESSOR_METHODS = new PublicAccessorVisibilityStrategy(); private static final PropertyVisibilityStrategy PUBLIC_FIELDS = new PublicFieldsVisibilityStrategy(); private static final PropertyVisibilityStrategy ALL_FIELDS_AND_METHODS = new AllFieldsVisibilityStrategy(); private VisibilityStrategiesProvider() { throw new IllegalStateException("This class cannot be instantiated"); } public static PropertyVisibilityStrategy getStrategy(String strategy) { switch (strategy) { case "PUBLIC_PROPERTY": return PUBLIC_PROPERTY; case "PUBLIC_ACCESSOR_METHODS": return PUBLIC_ACCESSOR_METHODS; case "PUBLIC_FIELDS": return PUBLIC_FIELDS; case "ALL_FIELD_AND_ACCESSORS": return ALL_FIELDS_AND_METHODS; default: throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_VISIBILITY_STRATEGY, strategy)); } } private static final class PublicPropertyVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return Modifier.isPublic(field.getModifiers()); } @Override public boolean isVisible(Method method) { return Modifier.isPublic(method.getModifiers()); } } private static final class PublicAccessorVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return false; } @Override public boolean isVisible(Method method) { return Modifier.isPublic(method.getModifiers()); } } private static final class PublicFieldsVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return Modifier.isPublic(field.getModifiers()); } @Override public boolean isVisible(Method method) { return false; } } private static final class AllFieldsVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return true; } @Override public boolean isVisible(Method method) { return true; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.properties; import java.beans.ConstructorProperties; import java.lang.reflect.ParameterizedType; import java.sql.Date; import jakarta.json.JsonNumber; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * Contains all message keys present in language property files. */ public enum MessageKeys { /** * An error has occurred while json deserialization. */ PROCESS_FROM_JSON("processFromJson"), /** * An error has occurred while object serialization. */ PROCESS_TO_JSON("processToJson"), /** * Object cannot be serialized. */ CANNOT_MARSHAL_OBJECT("cannotMarshallObject"), /** * An error has occurred during runtime type resolution. */ TYPE_RESOLUTION_ERROR("typeResolutionError"), /** * Generic bound not found. */ GENERIC_BOUND_NOT_FOUND("genericBoundNotFound"), /** * {@link JsonValue} could not be converted to some type. */ CANT_CONVERT_JSON_VALUE("cantConvertJsonValue"), /** * Root instance could not be created. */ CANT_CREATE_ROOT_INSTANCE("cantCreateRootInstance"), /** * Class does not have default constructor. */ NO_DEFAULT_CONSTRUCTOR("noDefaultConstructor"), /** * Class does not have default constructor. */ RECORD_MULTIPLE_CONSTRUCTORS("recordMultipleConstructors"), /** * There has been an error while invoking getter. */ INVOKING_GETTER("invokingGetter"), /** * Could not get field value. */ GETTING_VALUE("gettingValue"), /** * Could not set field value. */ SETTING_VALUE("settingValue"), /** * No logger name provided. */ NO_LOGGER_NAME("noLoggerName"), /** * {@link ParameterizedType} superclass could not be resolved. */ RESOLVE_PARAMETRIZED_TYPE("resolveParametrizedType"), /** * Instance could not be created. */ CANT_CREATE_INSTANCE("cantCreateInstance"), /** * Type could not be inferred to deserialization. */ INFER_TYPE_FOR_UNMARSHALL("inferTypeForUnmarshall"), /** * Implementation class is not compatible. */ IMPL_CLASS_INCOMPATIBLE("implClassIncompatible"), /** * Value is not of target type. */ NOT_VALUE_TYPE("notValueType"), /** * Unexpected parser event has occurred. */ UNEXPECTED_PARSE_EVENT("unexpectedParseEvent"), /** * Am error has occurred while creating handles. */ CREATING_HANDLES("creatingHandles"), /** * Could not get field value with method. */ GETTING_VALUE_WITH("gettingValueWith"), /** * Could not set field value with method. */ SETTING_VALUE_WITH("settingValueWith"), /** * String contains unpaired surrogate. */ UNPAIRED_SURROGATE("unpairedSurrogate"), /** * An exception occurred while adapting object. */ ADAPTER_EXCEPTION("adapterException"), /** * Adapter for current type has been found. */ ADAPTER_FOUND("adapterFound"), /** * Adapter is incompatible for current type. */ ADAPTER_INCOMPATIBLE("adapterIncompatible"), /** * Property order strategy not recognized. */ PROPERTY_ORDER("propertyOrder"), /** * Unsupported Jsonp serializer value. */ UNSUPPORTED_JSONP_SERIALIZER_VALUE("unsupportedJsonpSerializerValue"), /** * {@link JsonbConfig#FORMATTING} supports only Boolean types. */ JSONB_CONFIG_FORMATTING_ILLEGAL_VALUE("Only Boolean type values are supported for JsonbConfig.FORMATTING property."), /** * No JNDI provider found. */ NO_JNDI_ENVIRONMENT("noJndiEnvironment"), /** * CDI API provider has not been found. */ NO_CDI_API_PROVIDER("noCdiApiProvider"), /** * Insufficient permissions to access property. */ ILLEGAL_ACCESS("illegalAccess"), /** * CDI bean manager not found, serializers and adapters will not have CDI support. */ BEAN_MANAGER_NOT_FOUND_USING_DEFAULT("usingDefaultConstructorInstantiator"), /** * CDI environment is not available. */ NO_CDI_ENVIRONMENT("noCdiEnvironment"), /** * Cannot serialize single value due to I-Json support is enabled. */ IJSON_ENABLED_SINGLE_VALUE("iJsonEnabledSingleValue"), /** * Property not found in target class. */ PROPERTY_NOT_FOUND_DESERIALIZER("propertyNotFoundDeserializer"), /** * Property could not be set to target property. */ SETTING_PROPERTY_DESERIALIZER("settingPropertyDeserializer"), /** * Loading of specific class in not allowed. */ CLASS_LOAD_NOT_ALLOWED("classLoadNotAllowed"), /** * Data type is not supported. */ UNSUPPORTED_DATE_TYPE("dateTypeNotSupported"), /** * There has been an error during parsing number. */ DATE_PARSE_ERROR("errorParsingDate"), /** * Parsing offset date from epoch millisecond, UTC zone offset will be used. */ OFFSET_DATE_TIME_FROM_MILLIS("offsetDateTimeFromMillis"), /** * Target date object could not be converted to or from epoch millis. */ TIME_TO_EPOCH_MILLIS_ERROR("timeToEpochMillisError"), /** * Jsonb config property contains invalid type. */ JSONB_CONFIG_PROPERTY_INVALID_TYPE("configPropertyInvalidType"), /** * Conversion target type from or to String is not supported. */ CONVERSION_NOT_SUPPORTED("conversionNotSupported"), /** * End of the json structure reached. */ END_OF_JSON_STRUCTURE("endOfJsonStructure"), /** * Json value type could not be deserialized to the target type. */ INVALID_DESERIALIZATION_JSON_TYPE("invalidDeserializationType"), /** * An error occurred while calling {@link JsonbCreator}. */ ERROR_CALLING_JSONB_CREATOR("errorCallingJsonbCreator"), /** * Return type of the {@link JsonbCreator} has to be the same as target type. */ INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE("incompatibleFactoryCreatorReturnType"), /** * Only one {@link JsonbCreator} can be present in the class. */ MULTIPLE_JSONB_CREATORS("multipleJsonbCreators"), /** * An internal error has occurred. */ INTERNAL_ERROR("internalError"), /** * There has been an error during property serialization. */ SERIALIZE_PROPERTY_ERROR("serializePropertyError"), /** * There has been an error during value deserialization. */ DESERIALIZE_VALUE_ERROR("deserializeValueError"), /** * Number has unsupported format. */ PARSING_NUMBER("parsingNumber"), /** * Unknown binary data strategy selected. */ UNKNOWN_BINARY_DATA_STRATEGY("unknownBinaryDataStrategy"), /** * Invalid property naming strategy selected. */ PROPERTY_NAMING_STRATEGY_INVALID("invalidPropertyNamingStrategy"), /** * Creator parameter has to be annotated by {@link JsonbProperty} annotation. */ CREATOR_PARAMETER_NOT_ANNOTATED("creatorParameterNotAnnotated"), /** * Json property could not be mapped to the target class. */ UNKNOWN_JSON_PROPERTY("unknownJsonProperty"), /** * Json does not contain all necessary properties for {@link JsonbCreator}. */ JSONB_CREATOR_MISSING_PROPERTY("jsonbCreatorMissingProperty"), /** * There has been an error during zone deserialization. */ ZONE_PARSE_ERROR("zoneParseError"), /** * {@link JsonbTransient} was not the only annotation on class property. */ JSONB_TRANSIENT_WITH_OTHER_ANNOTATIONS("jsonbTransientWithOtherAnnotations"), /** * Target type is not {@link ParameterizedType}. */ NON_PARAMETRIZED_TYPE("nonParametrizedType"), /** * Handled property has the same read/write name in target class as some other property present there. */ PROPERTY_NAME_CLASH("propertyNameClash"), /** * {@link Date} is not supported I-Json is enabled. */ SQL_DATE_IJSON_ERROR("sqlDateIJsonError"), /** * Recursive reference detected. */ RECURSIVE_REFERENCE("recursiveReference"), /** * An error occurred while DatatypeFactory creation. */ DATATYPE_FACTORY_CREATION_FAILED("datatypeFactoryCreationFailed"), /** * Bean manager provider not found. */ BEAN_MANAGER_PROVIDER_NOT_FOUND("beanManagerProviderNotFound"), /** * More than one constructor annotated with {@link ConstructorProperties} declared in target class. */ MULTIPLE_CONSTRUCTOR_PROPERTIES_CREATORS("multipleConstructorPropertiesCreators"), /** * Target annotation is not visible in modules or classpath. */ ANNOTATION_NOT_AVAILABLE("annotationNotAvailable"), /** * Missing value property in target annotation. */ MISSING_VALUE_PROPERTY_IN_ANNOTATION("missingValuePropertyInAnnotation"), /** * Target json value is not valid {@link JsonNumber}. */ NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY("numberIncompatibleValueTypeArray"), /** * Target json value is not valid {@link JsonNumber}. */ NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT("numberIncompatibleValueTypeObject"), /** * Unknown visibility strategy. */ UNKNOWN_VISIBILITY_STRATEGY("unknownVisibilityStrategy"); /** * Message bundle key. */ private final String key; /** * Creates an instance of message bundle key. * * @param key Message key from bundle. */ MessageKeys(final String key) { this.key = key; } /** * Returns message bundle key. * * @return message bundle key */ public String getKey() { return key; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/properties/Messages.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.properties; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; import java.util.Locale; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; /** * JSON-B messages. */ public class Messages { private static final String MESSAGE_BUNDLE = "yasson-messages"; private static final String ENCODING = "UTF-8"; private Messages() { } /** * Gets message by key. Default locale is used. * * @param key Message key. * @param objects Message parameters. * @return Formatted message in string. */ public static String getMessage(MessageKeys key, Object... objects) { return getMessage(key, Locale.getDefault(), objects); } /** * Gets message by key and locale. * * @param key Message key. * @param locale Locale. * @param objects Message parameters. * @return Formatted message in string. */ public static String getMessage(MessageKeys key, Locale locale, Object... objects) { ResourceBundle messages = getResourceBundle(locale); MessageFormat formatter = new MessageFormat(messages.getString(key.getKey())); return formatter.format(objects); } /** * ResourceBundle.Control is not supported when loaded from JPMS native module. */ private static ResourceBundle getResourceBundle(Locale locale) { try { return ResourceBundle.getBundle(MESSAGE_BUNDLE, locale, new UTF8Control()); } catch (UnsupportedOperationException e) { return ResourceBundle.getBundle(MESSAGE_BUNDLE, locale); } } static class UTF8Control extends ResourceBundle.Control { public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { // The below is a copy of the default implementation. String bundleName = toBundleName(baseName, locale); String resourceName = toResourceName(bundleName, "properties"); ResourceBundle bundle = null; InputStream stream = null; if (reload) { URL url = loader.getResource(resourceName); if (url != null) { URLConnection connection = url.openConnection(); if (connection != null) { connection.setUseCaches(false); stream = connection.getInputStream(); } } } else { stream = loader.getResourceAsStream(resourceName); } if (stream != null) { try { // Only this line is changed to make it to read properties files as UTF-8. bundle = new PropertyResourceBundle(new InputStreamReader(stream, ENCODING)); } finally { stream.close(); } } return bundle; } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/AbstractSerializer.java ================================================ /* * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; /** * Abstract model serializer with delegate. */ abstract class AbstractSerializer implements ModelSerializer { final ModelSerializer delegate; AbstractSerializer(ModelSerializer delegate) { this.delegate = delegate; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.bind.JsonbException; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * User defined adapter invoker. */ class AdapterSerializer extends AbstractSerializer { private final JsonbAdapter adapter; private final AdapterBinding adapterBinding; @SuppressWarnings("unchecked") AdapterSerializer(AdapterBinding adapterBinding, ModelSerializer delegate) { super(delegate); this.adapter = (JsonbAdapter) adapterBinding.getAdapter(); this.adapterBinding = adapterBinding; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { try { delegate.serialize(adapter.adaptToJson(value), generator, context); } catch (Exception e) { throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION, adapterBinding.getBindingType(), adapterBinding.getToType(), adapter.getClass()), e); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/ArraySerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.util.Base64; import java.util.Map; import java.util.function.Function; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.BinaryDataStrategy; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Array container serializer. */ abstract class ArraySerializer implements ModelSerializer { private static final Map, Function> ARRAY_SERIALIZERS; static { ARRAY_SERIALIZERS = Map.of(boolean[].class, BooleanArraySerializer::new, byte[].class, ByteArraySerializer::new, char[].class, CharacterArraySerializer::new, double[].class, DoubleArraySerializer::new, float[].class, FloatArraySerializer::new, int[].class, IntegerArraySerializer::new, long[].class, LongArraySerializer::new, short[].class, ShortArraySerializer::new); } private final ModelSerializer valueSerializer; protected ArraySerializer(ModelSerializer valueSerializer) { this.valueSerializer = valueSerializer; } public static ModelSerializer create(Class arrayType, JsonbContext jsonbContext, ModelSerializer modelSerializer) { String binaryDataStrategy = jsonbContext.getConfigProperties().getBinaryDataStrategy(); if (byte[].class.equals(arrayType) && !binaryDataStrategy.equals(BinaryDataStrategy.BYTE)) { return new Base64ByteArraySerializer(binaryDataStrategy); } if (ARRAY_SERIALIZERS.containsKey(arrayType)) { return ARRAY_SERIALIZERS.get(arrayType).apply(modelSerializer); } return new ObjectArraySerializer(modelSerializer); } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { generator.writeStartArray(); serializeArray(value, generator, context); generator.writeEnd(); } abstract void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context); protected ModelSerializer getValueSerializer() { return valueSerializer; } private static final class ByteArraySerializer extends ArraySerializer { ByteArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { byte[] array = (byte[]) value; for (byte b : array) { getValueSerializer().serialize(b, generator, context); } } } private static final class Base64ByteArraySerializer implements ModelSerializer { private final Base64.Encoder encoder; Base64ByteArraySerializer(String strategy) { this.encoder = getEncoder(strategy); } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { byte[] array = (byte[]) value; generator.write(encoder.encodeToString(array)); } private Base64.Encoder getEncoder(String strategy) { switch (strategy) { case BinaryDataStrategy.BASE_64: return Base64.getEncoder(); case BinaryDataStrategy.BASE_64_URL: return Base64.getUrlEncoder(); default: throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Invalid strategy: " + strategy)); } } } private static final class ShortArraySerializer extends ArraySerializer { ShortArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { short[] array = (short[]) value; for (short s : array) { getValueSerializer().serialize(s, generator, context); } } } private static final class IntegerArraySerializer extends ArraySerializer { IntegerArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { int[] array = (int[]) value; for (int i : array) { getValueSerializer().serialize(i, generator, context); } } } private static final class LongArraySerializer extends ArraySerializer { LongArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { long[] array = (long[]) value; for (long l : array) { getValueSerializer().serialize(l, generator, context); } } } private static final class FloatArraySerializer extends ArraySerializer { FloatArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { float[] array = (float[]) value; for (float f : array) { getValueSerializer().serialize(f, generator, context); } } } private static final class DoubleArraySerializer extends ArraySerializer { DoubleArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { double[] array = (double[]) value; for (double d : array) { getValueSerializer().serialize(d, generator, context); } } } private static final class BooleanArraySerializer extends ArraySerializer { BooleanArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { boolean[] array = (boolean[]) value; for (boolean b : array) { getValueSerializer().serialize(b, generator, context); } } } private static final class CharacterArraySerializer extends ArraySerializer { CharacterArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { char[] array = (char[]) value; for (char c : array) { getValueSerializer().serialize(c, generator, context); } } } private static final class ObjectArraySerializer extends ArraySerializer { ObjectArraySerializer(ModelSerializer valueSerializer) { super(valueSerializer); } @Override public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) { Object[] array = (Object[]) value; for (Object o : array) { getValueSerializer().serialize(o, generator, context); } } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/CollectionSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.util.Collection; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Collection container serializer. */ class CollectionSerializer implements ModelSerializer { private final ModelSerializer delegate; CollectionSerializer(ModelSerializer delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { Collection collection = (Collection) value; generator.writeStartArray(); collection.forEach(object -> delegate.serialize(object, generator, context)); generator.writeEnd(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/CyclicReferenceSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.lang.reflect.Type; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Solution for cyclic references in serialization. * This approach helps us to avoid creation of multiple serializers for the same type. */ class CyclicReferenceSerializer implements ModelSerializer { private final Type type; private ModelSerializer delegate; CyclicReferenceSerializer(Type type) { this.type = type; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (delegate == null) { delegate = context.getJsonbContext().getSerializationModelCreator().serializerChain(type, true, true); } delegate.serialize(value, generator, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/KeyWriter.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Key name writer. Writes key name of the property if present. */ public class KeyWriter implements ModelSerializer { private final ModelSerializer delegate; /** * Create new instance. * * @param delegate delegate to be called after the key is written */ public KeyWriter(ModelSerializer delegate) { this.delegate = delegate; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (context.getKey() != null) { generator.writeKey(context.getKey()); context.setKey(null); } delegate.serialize(value, generator, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/MapSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.util.Map; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.serializer.types.TypeSerializers; /** * Map container serializer. */ abstract class MapSerializer implements ModelSerializer { private final ModelSerializer keySerializer; private final ModelSerializer valueSerializer; MapSerializer(ModelSerializer keySerializer, ModelSerializer valueSerializer) { this.keySerializer = keySerializer; this.valueSerializer = valueSerializer; } ModelSerializer getKeySerializer() { return keySerializer; } ModelSerializer getValueSerializer() { return valueSerializer; } static MapSerializer create(Class keyClass, ModelSerializer keySerializer, ModelSerializer valueSerializer, JsonbContext jsonbContext) { if (TypeSerializers.isSupportedMapKey(keyClass)) { //Issue #663: A custom JsonbSerializer is available for an already supported Map key. Serialization must //not use normal key:value map. No further checking needed. Wrapping object needs to be used. if (TypeSerializers.hasCustomJsonbSerializer(keyClass, jsonbContext)) { return new ObjectKeyMapSerializer(keySerializer, valueSerializer); } else { return new StringKeyMapSerializer(keySerializer, valueSerializer); } } else if (Object.class.equals(keyClass)) { return new DynamicMapSerializer(keySerializer, valueSerializer); } return new ObjectKeyMapSerializer(keySerializer, valueSerializer); } private static final class DynamicMapSerializer extends MapSerializer { private final StringKeyMapSerializer stringMap; private final ObjectKeyMapSerializer objectMap; private MapSerializer serializer; DynamicMapSerializer(ModelSerializer keySerializer, ModelSerializer valueSerializer) { super(keySerializer, valueSerializer); stringMap = new StringKeyMapSerializer(keySerializer, valueSerializer); objectMap = new ObjectKeyMapSerializer(keySerializer, valueSerializer); } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (serializer == null) { //We have to be sure that Map with Object as a key contains only supported values for key:value format map. Map map = (Map) value; boolean suitable = true; for (Object key : map.keySet()) { if (key == null) { if (context.getJsonbContext().getConfigProperties().isForceMapArraySerializerForNullKeys()) { suitable = false; break; } continue; } Class keyClass = key.getClass(); if (TypeSerializers.isSupportedMapKey(keyClass)) { //Issue #663: A custom JsonbSerializer is available for an already supported Map key. //Serialization must not use normal key:value map. No further checking needed. Wrapping object //needs to be used. if (TypeSerializers.hasCustomJsonbSerializer(keyClass, context.getJsonbContext())) { suitable = false; break; } else { continue; } } //No other checks needed. Map is not suitable for normal key:value map. Wrapping object needs to be used. suitable = false; break; } serializer = suitable ? stringMap : objectMap; } serializer.serialize(value, generator, context); } } private static final class StringKeyMapSerializer extends MapSerializer { StringKeyMapSerializer(ModelSerializer keySerializer, ModelSerializer valueSerializer) { super(keySerializer, valueSerializer); } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { Map map = (Map) value; generator.writeStartObject(); map.forEach((key, val) -> { getKeySerializer().serialize(key, generator, context); getValueSerializer().serialize(val, generator, context); }); generator.writeEnd(); } } private static final class ObjectKeyMapSerializer extends MapSerializer { ObjectKeyMapSerializer(ModelSerializer keySerializer, ModelSerializer valueSerializer) { super(keySerializer, valueSerializer); } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { Map map = (Map) value; generator.writeStartArray(); map.forEach((key, val) -> { generator.writeStartObject(); generator.writeKey("key"); if (key == null) { generator.writeNull(); } else { getKeySerializer().serialize(key, generator, context); } generator.writeKey("value"); getValueSerializer().serialize(val, generator, context); generator.writeEnd(); }); generator.writeEnd(); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/ModelSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Type serializer. *
* All the instances are required to be reusable and without any states * stored in the class fields. */ public interface ModelSerializer { /** * Serialize provided value or delegate serialization to the next serializer. * * @param value value to be serialized * @param generator json generator * @param context serialization context */ void serialize(Object value, JsonGenerator generator, SerializationContextImpl context); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/NullSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.model.customization.Customization; /** * Null value serializer. Determines proper behavior when the serialized value is null. */ public class NullSerializer implements ModelSerializer { private final ModelSerializer delegate; private final ModelSerializer nullSerializer; private final ModelSerializer rootNullSerializer; /** * Create new instance. * * @param delegate non-null value delegate * @param customization component customization * @param jsonbContext jsonb context */ public NullSerializer(ModelSerializer delegate, Customization customization, JsonbContext jsonbContext) { this.delegate = delegate; if (customization.isNillable()) { nullSerializer = new NullWritingEnabled(); } else { nullSerializer = new NullWritingDisabled(); } JsonbSerializer userDefinedNullSerializer = jsonbContext.getConfigProperties().getNullSerializer(); if (userDefinedNullSerializer != null) { rootNullSerializer = (value, generator, context) -> userDefinedNullSerializer.serialize(null, generator, context); } else { rootNullSerializer = nullSerializer; } } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (value == null) { if (context.isRoot()) { context.setRoot(false); rootNullSerializer.serialize(null, generator, context); } else { nullSerializer.serialize(null, generator, context); } context.setKey(null); } else { context.setRoot(false); delegate.serialize(value, generator, context); } } private static final class NullWritingEnabled implements ModelSerializer { @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (context.getKey() == null) { generator.writeNull(); } else { generator.writeNull(context.getKey()); } } } private static class NullWritingDisabled implements ModelSerializer { @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (context.isContainerWithNulls()) { if (context.getKey() == null) { generator.writeNull(); } else { generator.writeNull(context.getKey()); } } context.setKey(null); //Do nothing } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/NullVisibilitySwitcher.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Switching mechanism for default null value visibility in the JSON. * * Some constructs such as arrays, collections etc. require to have nulls serialized into the JSON by default. * This class switches from the default parent null visibility to the current construct visibility. As soon as the current * construct is serialized, visibility is switched back to the parent ones. */ class NullVisibilitySwitcher implements ModelSerializer { private final boolean nullsEnabled; private final ModelSerializer delegate; NullVisibilitySwitcher(boolean nullsEnabled, ModelSerializer delegate) { this.nullsEnabled = nullsEnabled; this.delegate = delegate; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { boolean previous = context.isContainerWithNulls(); context.setContainerWithNulls(nullsEnabled); delegate.serialize(value, generator, context); context.setContainerWithNulls(previous); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.util.LinkedHashMap; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Object container serializer. */ class ObjectSerializer implements ModelSerializer { private final LinkedHashMap propertySerializers; ObjectSerializer(LinkedHashMap propertySerializers) { this.propertySerializers = propertySerializers; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { generator.writeStartObject(); propertySerializers.forEach((key, serializer) -> { try { context.setKey(key); serializer.serialize(value, generator, context); } catch (Exception e) { throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_PROPERTY_ERROR, key, value.getClass().getCanonicalName()), e); } }); generator.writeEnd(); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/OptionalSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.util.Optional; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Optional container serializer. */ class OptionalSerializer implements ModelSerializer { private final ModelSerializer delegate; OptionalSerializer(ModelSerializer delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { Optional optional = (Optional) value; delegate.serialize(optional == null ? null : optional.orElse(null), generator, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/RecursionChecker.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Recursion checker serializer deals with possible instance recursion in instances. */ class RecursionChecker implements ModelSerializer { private final ModelSerializer delegate; RecursionChecker(ModelSerializer delegate) { this.delegate = delegate; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { if (!context.addProcessedObject(value)) { throw new JsonbException(Messages.getMessage(MessageKeys.RECURSIVE_REFERENCE, value.getClass())); } delegate.serialize(value, generator, context); context.removeProcessedObject(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java ================================================ /* * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.ComponentMatcher; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.ReflectionUtils; import org.eclipse.yasson.internal.components.AdapterBinding; import org.eclipse.yasson.internal.components.SerializerBinding; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.PropertyModel; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer; import org.eclipse.yasson.internal.serializer.types.TypeSerializers; /** * Create or obtain already created type serializer. */ public class SerializationModelCreator { private final Map explicitChain = new ConcurrentHashMap<>(); private final Map dynamicChain = new ConcurrentHashMap<>(); private final JsonbContext jsonbContext; /** * Create new instance. * * @param jsonbContext jsonb context */ public SerializationModelCreator(JsonbContext jsonbContext) { this.jsonbContext = jsonbContext; } /** * Wrap {@link ModelSerializer} in the common set of serializers. * * @param modelSerializer serializer to be wrapped * @param customization component customization * @param jsonbContext jsonb context * @return wrapped serializer */ public static ModelSerializer wrapInCommonSet(ModelSerializer modelSerializer, Customization customization, JsonbContext jsonbContext) { KeyWriter serializer = new KeyWriter(modelSerializer); return new NullSerializer(serializer, customization, jsonbContext); } /** * Create new {@link ModelSerializer} of the given type. * * @param type type to be serialized * @param rootValue whether it is a root value * @param resolveRootAdapter whether to resolve root adapter * @return type model serializer */ public ModelSerializer serializerChain(Type type, boolean rootValue, boolean resolveRootAdapter) { Class rawType = ReflectionUtils.getRawType(type); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType); LinkedList chain = new LinkedList<>(); return serializerChain(chain, type, classModel.getClassCustomization(), rootValue, false, resolveRootAdapter); } /** * Create new {@link ModelSerializer} of the given type. * * @param chain chain of types used before the one currently processed * @param type type to be serialized * @param propertyCustomization component customization * @param rootValue whether it is a root value * @param isKey whether it is a key * @return type model serializer */ public ModelSerializer serializerChainRuntime(LinkedList chain, Type type, Customization propertyCustomization, boolean rootValue, boolean isKey) { if (chain.contains(type)) { return new CyclicReferenceSerializer(type); } //If the class instance and class of the field are the same and there has been generics specified for this field, //we need to use those instead of raw type. Class rawType = ReflectionUtils.getRawType(type); Class rawLast = ReflectionUtils.getRawType(chain.getLast()); if (rawLast.equals(rawType)) { return serializerChainInternal(chain, chain.getLast(), propertyCustomization, rootValue, isKey, true); } return serializerChainInternal(chain, type, propertyCustomization, rootValue, isKey, true); } private ModelSerializer serializerChain(LinkedList chain, Type type, Customization propertyCustomization, boolean rootValue, boolean isKey, boolean resolveRootAdapter) { if (chain.contains(type)) { return new CyclicReferenceSerializer(type); } try { chain.add(type); return serializerChainInternal(chain, type, propertyCustomization, rootValue, isKey, resolveRootAdapter); } finally { chain.removeLast(); } } private ModelSerializer serializerChainInternal(LinkedList chain, Type type, Customization propertyCustomization, boolean rootValue, boolean isKey, boolean resolveRootAdapter) { if (explicitChain.containsKey(type)) { return explicitChain.get(type); } Class rawType = ReflectionUtils.getRawType(type); Optional serializerBinding = userSerializer(type, (ComponentBoundCustomization) propertyCustomization); if (serializerBinding.isPresent()) { return serializerBinding.get(); } if (resolveRootAdapter) { Optional maybeAdapter = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization); if (maybeAdapter.isPresent()) { AdapterBinding adapterBinding = maybeAdapter.get(); Type toType = adapterBinding.getToType(); Class rawToType = ReflectionUtils.getRawType(toType); ModelSerializer typeSerializer = TypeSerializers .getTypeSerializer(rawToType, propertyCustomization, jsonbContext); if (typeSerializer == null) { typeSerializer = serializerChain(toType, rootValue, !type.equals(toType)); } AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer); RecursionChecker recursionChecker = new RecursionChecker(adapterSerializer); NullSerializer nullSerializer = new NullSerializer(recursionChecker, propertyCustomization, jsonbContext); explicitChain.put(type, nullSerializer); return nullSerializer; } } ModelSerializer typeSerializer = null; if (!Object.class.equals(rawType)) { typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, propertyCustomization, jsonbContext, isKey); } if (typeSerializer != null) { if (jsonbContext.getConfigProperties().isStrictIJson() && rootValue) { throw new JsonbException(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE)); } return typeSerializer; } ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType); if (Collection.class.isAssignableFrom(rawType)) { return createCollectionSerializer(chain, type, propertyCustomization); } else if (Map.class.isAssignableFrom(rawType)) { return createMapSerializer(chain, type, propertyCustomization); } else if (rawType.isArray()) { return createArraySerializer(chain, rawType, propertyCustomization); } else if (type instanceof GenericArrayType) { return createGenericArraySerializer(chain, type, propertyCustomization); } else if (Optional.class.equals(rawType)) { return createOptionalSerializer(chain, type, propertyCustomization, isKey); } return createObjectSerializer(chain, type, classModel); } private ModelSerializer createObjectSerializer(LinkedList chain, Type type, ClassModel classModel) { LinkedHashMap propertySerializers = new LinkedHashMap<>(); TypeInheritanceConfiguration typeInheritanceConfiguration = classModel.getClassCustomization().getPolymorphismConfig(); if (typeInheritanceConfiguration != null) { addPolymorphismProperty(typeInheritanceConfiguration, propertySerializers, classModel); } for (PropertyModel model : classModel.getSortedProperties()) { if (model.isReadable()) { String name = model.getWriteName(); ModelSerializer memberModel = memberSerializer(chain, model.getPropertySerializationType(), model.getCustomization(), false); propertySerializers.put(name, new ValueGetterSerializer(model.getGetValueHandle(), memberModel)); } } ModelSerializer objectSerializer = new ObjectSerializer(propertySerializers); RecursionChecker recursionChecker = new RecursionChecker(objectSerializer); KeyWriter keyWriter = new KeyWriter(recursionChecker); NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(false, keyWriter); NullSerializer nullSerializer = new NullSerializer(nullVisibilitySwitcher, classModel.getClassCustomization(), jsonbContext); explicitChain.put(type, nullSerializer); return nullSerializer; } private void addPolymorphismProperty(TypeInheritanceConfiguration typeInheritanceConfiguration, LinkedHashMap propertySerializers, ClassModel classModel) { Class rawType = classModel.getType(); String alias = typeInheritanceConfiguration.getAliases().get(rawType); ModelSerializer serializer = createPolymorphismPropertySerializer(typeInheritanceConfiguration, alias); if ((!typeInheritanceConfiguration.isInherited() || alias != null) && typeInheritanceConfiguration.getParentConfig() != null) { addParentPolymorphismProperty(typeInheritanceConfiguration.getParentConfig(), propertySerializers, classModel); } if (serializer != null) { propertySerializers.put(typeInheritanceConfiguration.getFieldName(), serializer); } for (PropertyModel propertyModel : classModel.getSortedProperties()) { if (propertySerializers.containsKey(propertyModel.getWriteName())) { throw new JsonbException("CHANGE naming conflict!"); } } } private void addParentPolymorphismProperty(TypeInheritanceConfiguration typeInheritanceConfiguration, LinkedHashMap propertySerializers, ClassModel classModel) { Class rawType = classModel.getType(); TypeInheritanceConfiguration current = typeInheritanceConfiguration; LinkedHashMap toBeAdded = new LinkedHashMap<>(); while (current != null) { TypeInheritanceConfiguration local = current; String alias = local.getAliases().entrySet().stream() .filter(entry -> entry.getKey().isAssignableFrom(rawType)) .map(Map.Entry::getValue) .findFirst() .orElse(null); if (alias != null) { ModelSerializer serializer = createPolymorphismPropertySerializer(local, alias); toBeAdded.put(current.getFieldName(), serializer); current = current.getParentConfig(); } } ListIterator> iterator = new ArrayList<>(toBeAdded.entrySet()) .listIterator(toBeAdded.size()); while (iterator.hasPrevious()) { Map.Entry entry = iterator.previous(); propertySerializers.put(entry.getKey(), entry.getValue()); } } private ModelSerializer createPolymorphismPropertySerializer(TypeInheritanceConfiguration configuration, String alias) { if (alias != null) { return (value, generator, context) -> generator.write(configuration.getFieldName(), alias); } return null; } private ModelSerializer createCollectionSerializer(LinkedList chain, Type type, Customization customization) { Type colType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; Type resolvedKey = ReflectionUtils.resolveType(chain, colType); Class rawClass = ReflectionUtils.getRawType(resolvedKey); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawClass); ModelSerializer typeSerializer = memberSerializer(chain, colType, classModel.getClassCustomization(), false); CollectionSerializer collectionSerializer = new CollectionSerializer(typeSerializer); KeyWriter keyWriter = new KeyWriter(collectionSerializer); NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter); return new NullSerializer(nullVisibilitySwitcher, customization, jsonbContext); } private ModelSerializer createMapSerializer(LinkedList chain, Type type, Customization propertyCustomization) { Type keyType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; Type valueType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[1] : Object.class; Type resolvedKey = ReflectionUtils.resolveType(chain, keyType); Class rawClass = ReflectionUtils.getRawType(resolvedKey); ModelSerializer keySerializer = memberSerializer(chain, keyType, ClassCustomization.empty(), true); Type resolvedValue = ReflectionUtils.resolveType(chain, valueType); ClassModel valueClassModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(resolvedValue)); ModelSerializer valueSerializer = memberSerializer(chain, valueType, valueClassModel.getClassCustomization(), false); MapSerializer mapSerializer = MapSerializer.create(rawClass, keySerializer, valueSerializer, jsonbContext); KeyWriter keyWriter = new KeyWriter(mapSerializer); NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter); return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext); } private ModelSerializer createArraySerializer(LinkedList chain, Class raw, Customization propertyCustomization) { Class arrayComponent = raw.getComponentType(); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(arrayComponent); ModelSerializer modelSerializer = memberSerializer(chain, arrayComponent, classModel.getClassCustomization(), false); ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer); KeyWriter keyWriter = new KeyWriter(arraySerializer); NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter); return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext); } private ModelSerializer createGenericArraySerializer(LinkedList chain, Type type, Customization propertyCustomization) { Class raw = ReflectionUtils.getRawType(type); Class component = ReflectionUtils.getRawType(((GenericArrayType) type).getGenericComponentType()); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(component); ModelSerializer modelSerializer = memberSerializer(chain, component, classModel.getClassCustomization(), false); ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer); KeyWriter keyWriter = new KeyWriter(arraySerializer); NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter); return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext); } private ModelSerializer createOptionalSerializer(LinkedList chain, Type type, Customization propertyCustomization, boolean isKey) { Type optType = type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[0] : Object.class; Type resolvedOptType = ReflectionUtils.resolveType(chain, optType); ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(resolvedOptType)); ModelSerializer modelSerializer = memberSerializer(chain, optType, classModel.getClassCustomization(), isKey); return new OptionalSerializer(modelSerializer); } private ModelSerializer memberSerializer(LinkedList chain, Type type, Customization customization, boolean key) { Type resolved = ReflectionUtils.resolveType(chain, type); Class rawType = ReflectionUtils.getRawType(resolved); Optional serializerBinding = userSerializer(resolved, (ComponentBoundCustomization) customization); if (serializerBinding.isPresent()) { return serializerBinding.get(); } Optional maybeAdapter = adapterBinding(resolved, (ComponentBoundCustomization) customization); if (maybeAdapter.isPresent()) { AdapterBinding adapterBinding = maybeAdapter.get(); Type toType = adapterBinding.getToType(); Class rawToType = ReflectionUtils.getRawType(toType); ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(rawToType, customization, jsonbContext); if (typeSerializer == null) { typeSerializer = serializerChain(toType, false, true); } AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer); return new NullSerializer(adapterSerializer, customization, jsonbContext); } ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, customization, jsonbContext, key); if (typeSerializer == null) { //Final classes dont have any child classes. It is safe to assume that there will be instance of that specific class. boolean isFinal = Modifier.isFinal(rawType.getModifiers()); if (isFinal || Collection.class.isAssignableFrom(rawType) || Map.class.isAssignableFrom(rawType)) { return serializerChain(chain, resolved, customization, false, key, true); } else { if (dynamicChain.containsKey(resolved)) { return dynamicChain.get(resolved); } boolean isAbstract = Modifier.isAbstract(rawType.getModifiers()); ModelSerializer specificTypeSerializer = null; if (!isAbstract && !rawType.equals(Object.class)) { if (explicitChain.containsKey(resolved)) { specificTypeSerializer = explicitChain.get(resolved); } else { specificTypeSerializer = serializerChain(chain, resolved, customization, false, key, true); } } //Needs to be dynamically resolved with special cache since possible inheritance problem. if (resolved instanceof Class) { typeSerializer = TypeSerializers.getTypeSerializer(chain, Object.class, customization, jsonbContext, key); } else { chain.add(resolved); typeSerializer = TypeSerializers.getTypeSerializer(chain, Object.class, customization, jsonbContext, key); chain.removeLast(); } if (specificTypeSerializer != null && typeSerializer instanceof ObjectTypeSerializer) { ((ObjectTypeSerializer) typeSerializer).addSpecificSerializer(rawType, specificTypeSerializer); } //Since typeSerializer is handled as Object currently, we need to wrap it with null checker (if it is not a key) if (!key) { typeSerializer = new NullSerializer(typeSerializer, customization, jsonbContext); } dynamicChain.put(type, typeSerializer); } } if (!key && typeSerializer instanceof ObjectTypeSerializer) { typeSerializer = new NullSerializer(typeSerializer, customization, jsonbContext); } return typeSerializer; } private Optional userSerializer(Type type, ComponentBoundCustomization classCustomization) { final ComponentMatcher componentMatcher = jsonbContext.getComponentMatcher(); return componentMatcher.getSerializerBinding(type, classCustomization) .map(SerializerBinding::getJsonbSerializer) .map(UserDefinedSerializer::new) .map(RecursionChecker::new) .map(serializer -> SerializationModelCreator.wrapInCommonSet(serializer, (Customization) classCustomization, jsonbContext)); } private Optional adapterBinding(Type type, ComponentBoundCustomization classCustomization) { return jsonbContext.getComponentMatcher().getSerializeAdapterBinding(type, classCustomization); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/SerializerBuilderParams.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.lang.reflect.Type; import java.util.Objects; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.eclipse.yasson.internal.model.customization.Customization; /** * Not currently supported. Possibly implemented in the future. * * Holder of serialization parameters during creation process. Reduces the number of needed parameters. */ class SerializerBuilderParams { private final Type type; private final Customization customization; private final boolean root; private final boolean key; private final boolean resolveRootAdapter; private final ModelSerializer objectBaseSerializer; private SerializerBuilderParams(Builder builder) { this.type = builder.type; this.customization = builder.customization; this.root = builder.root; this.key = builder.key; this.resolveRootAdapter = builder.resolveRootAdapter; this.objectBaseSerializer = builder.objectBaseSerializer; } public static Builder builder(Type type) { return new Builder(type); } public Type getType() { return type; } public Customization getCustomization() { return customization; } public boolean isRoot() { return root; } public boolean isKey() { return key; } public boolean isResolveRootAdapter() { return resolveRootAdapter; } public ModelSerializer getObjectBaseSerializer() { return objectBaseSerializer; } static final class Builder { private Type type; private Customization customization; private boolean root; private boolean key; private boolean resolveRootAdapter; private ModelSerializer objectBaseSerializer; private Builder(Type type) { this.type = Objects.requireNonNull(type); this.customization = ClassCustomization.empty(); this.root = true; this.key = false; } public Builder type(Type type) { this.type = Objects.requireNonNull(type); return this; } public Builder customization(Customization customization) { this.customization = Objects.requireNonNull(customization); return this; } public Builder root(boolean root) { this.root = root; return this; } public Builder key(boolean key) { this.key = key; return this; } public Builder resolveRootAdapter(boolean resolveRootAdapter) { this.resolveRootAdapter = resolveRootAdapter; return this; } public Builder objectBaseSerializer(ModelSerializer objectBaseSerializer) { this.objectBaseSerializer = objectBaseSerializer; return this; } public SerializerBuilderParams build() { return new SerializerBuilderParams(this); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/UserDefinedSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * User defined serializer executor. */ class UserDefinedSerializer implements ModelSerializer { private final JsonbSerializer userDefinedSerializer; UserDefinedSerializer(JsonbSerializer userDefinedSerializer) { this.userDefinedSerializer = userDefinedSerializer; } @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { YassonGenerator yassonGenerator = new YassonGenerator(generator); userDefinedSerializer.serialize((T) value, yassonGenerator, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/ValueGetterSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.lang.invoke.MethodHandle; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Extractor of the serialized value from the instance. */ class ValueGetterSerializer implements ModelSerializer { private final MethodHandle valueGetter; private final ModelSerializer delegate; ValueGetterSerializer(MethodHandle valueGetter, ModelSerializer delegate) { this.valueGetter = valueGetter; this.delegate = delegate; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { Object object; try { object = valueGetter.invoke(value); } catch (Throwable e) { throw new JsonbException("Error getting value on: " + value.getClass().getName(), e); } delegate.serialize(object, generator, context); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/YassonGenerator.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import java.math.BigDecimal; import java.math.BigInteger; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; /** * Yasson {@link JsonGenerator} generator wrapper. *
* Used for user defined serializers. Does not allow serializer to write outside the scope it should be used on. */ class YassonGenerator implements JsonGenerator { private final JsonGenerator delegate; private int level; YassonGenerator(JsonGenerator delegate) { this.delegate = delegate; } @Override public JsonGenerator writeStartObject() { writeValidate("writeStartObject()"); level++; delegate.writeStartObject(); return this; } @Override public JsonGenerator writeStartObject(String name) { writeValidate("writeStartObject(String name)"); level++; delegate.writeStartObject(name); return this; } @Override public JsonGenerator writeKey(String name) { writeValidate("writeKey(String name)"); delegate.writeKey(name); return this; } @Override public JsonGenerator writeStartArray() { writeValidate("writeStartArray()"); level++; delegate.writeStartArray(); return this; } @Override public JsonGenerator writeStartArray(String name) { writeValidate("writeStartArray(String name)"); level++; delegate.writeStartArray(name); return this; } @Override public JsonGenerator write(String name, JsonValue value) { writeValidate("write(String name, JsonValue value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, String value) { writeValidate("write(String name, String value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, BigInteger value) { writeValidate("write(String name, BigInteger value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, BigDecimal value) { writeValidate("write(String name, BigDecimal value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, int value) { writeValidate("write(String name, int value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, long value) { writeValidate("write(String name, long value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, double value) { writeValidate("write(String name, double value)"); delegate.write(name, value); return this; } @Override public JsonGenerator write(String name, boolean value) { writeValidate("write(String name, boolean value)"); delegate.write(name, value); return this; } @Override public JsonGenerator writeNull(String name) { writeValidate("writeNull(String name)"); delegate.writeNull(name); return this; } @Override public JsonGenerator writeEnd() { level--; if (level < 0) { throw new JsonbException("writeEnd() cannot be called outside of the scope of user generator."); } if (level == 0) { level--; //if user has closed array or object and is on the same level he started. There is no more allowed writing. } delegate.writeEnd(); return this; } @Override public JsonGenerator write(JsonValue value) { writeValidate("write(JsonValue value)"); delegate.write(value); return this; } @Override public JsonGenerator write(String value) { writeValidate("write(String value)"); delegate.write(value); return this; } @Override public JsonGenerator write(BigDecimal value) { writeValidate("write(BigDecimal value)"); delegate.write(value); return this; } @Override public JsonGenerator write(BigInteger value) { writeValidate("write(BigInteger value)"); delegate.write(value); return this; } @Override public JsonGenerator write(int value) { writeValidate("write(int value)"); delegate.write(value); return this; } @Override public JsonGenerator write(long value) { writeValidate("write(long value)"); delegate.write(value); return this; } @Override public JsonGenerator write(double value) { writeValidate("write(double value)"); delegate.write(value); return this; } @Override public JsonGenerator write(boolean value) { writeValidate("write(boolean value)"); delegate.write(value); return this; } @Override public JsonGenerator writeNull() { writeValidate("writeNull()"); delegate.writeNull(); return this; } @Override public void close() { throw new JsonbException("Unsupported operation in user defined serializer."); } @Override public void flush() { throw new JsonbException("Unsupported operation in user defined serializer."); } private void writeValidate(String method) { if (level < 0) { throw new JsonbException(method + " cannot be called outside of the scope of user generator."); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractDateSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Locale; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.JsonbDateFormatter; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.model.customization.Customization; /** * Base for all date related serializers. */ abstract class AbstractDateSerializer extends TypeSerializer { static final ZoneId UTC = ZoneId.of("UTC"); private final Function toStringSerializer; private final BiConsumer valueWriter; AbstractDateSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); Customization customization = serializerBuilder.getCustomization(); JsonbConfigProperties properties = serializerBuilder.getJsonbContext().getConfigProperties(); final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization); toStringSerializer = valueSerializer(serializerBuilder); if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat()) && !properties.isDateInMillisecondsAsString()) { valueWriter = (value, generator) -> generator.write(toInstant(value).toEpochMilli()); } else { valueWriter = (value, generator) -> generator.write(toStringSerializer.apply(value)); } } private Function valueSerializer(TypeSerializerBuilder serializerBuilder) { Customization customization = serializerBuilder.getCustomization(); JsonbConfigProperties properties = serializerBuilder.getJsonbContext().getConfigProperties(); final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization); if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat())) { return value -> String.valueOf(toInstant(value).toEpochMilli()); } else if (formatter.getDateTimeFormatter() != null) { DateTimeFormatter dateTimeFormatter = formatter.getDateTimeFormatter(); return value -> formatWithFormatter(value, dateTimeFormatter); } else { DateTimeFormatter configDateTimeFormatter = properties.getConfigDateFormatter().getDateTimeFormatter(); if (configDateTimeFormatter != null) { return value -> formatWithFormatter(value, configDateTimeFormatter); } } if (properties.isStrictIJson()) { return this::formatStrictIJson; } Locale locale = properties.getLocale(formatter.getLocale()); return value -> formatDefault(value, locale); } private JsonbDateFormatter getJsonbDateFormatter(JsonbConfigProperties properties, Customization customization) { return Optional.ofNullable(customization.getSerializeDateFormatter()) .orElse(properties.getConfigDateFormatter()); } /** * Convert date object to {@link TemporalAccessor} * * Only for legacy dates. * * @param value date object * @return converted {@link TemporalAccessor} */ protected TemporalAccessor toTemporalAccessor(T value) { return (TemporalAccessor) value; } /** * Convert java.time object to epoch milliseconds instant. Discards zone offset and zone id information. * * @param value date object to convert * @return instant */ protected abstract Instant toInstant(T value); /** * Format with default formatter for a given java.time date object. * Different default formatter for each date object type is used. * * @param value date object * @param locale locale from annotation / default not null * @return formatted date obj as string */ protected abstract String formatDefault(T value, Locale locale); /** * Format date object with given formatter. * * @param value date object to format * @param formatter formatter to format with * @return formatted result */ protected String formatWithFormatter(T value, DateTimeFormatter formatter) { return formatter.format(toTemporalAccessor(value)); } /** * Format date object as strict IJson date format. * * @param value value to format * @return formatted result */ protected String formatStrictIJson(T value) { return JsonbDateFormatter.IJSON_DATE_FORMATTER.format(toTemporalAccessor(value)); } /** * Append UTC zone in case zone is not set on formatter. * * @param formatter formatter * @return zoned formatter */ protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) { return formatter.getZone() != null ? formatter : formatter.withZone(UTC); } @Override void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context) { valueWriter.accept(value, generator); } @Override void serializeKey(T key, JsonGenerator generator, SerializationContextImpl context) { generator.writeKey(toStringSerializer.apply(key)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractNumberSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.text.DecimalFormat; import java.text.NumberFormat; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.JsonbNumberFormatter; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * Base for all number related serializers. */ abstract class AbstractNumberSerializer extends TypeSerializer { private final ModelSerializer actualSerializer; AbstractNumberSerializer(TypeSerializerBuilder builder) { super(builder); actualSerializer = actualSerializer(builder.getCustomization(), builder.getJsonbContext()); } @SuppressWarnings("unchecked") private ModelSerializer actualSerializer(Customization customization, JsonbContext jsonbContext) { JsonbNumberFormatter formatter = customization.getSerializeNumberFormatter(); if (formatter == null) { return (value, generator, context) -> writeValue((T) value, generator); } final NumberFormat format = NumberFormat .getInstance(jsonbContext.getConfigProperties().getLocale(formatter.getLocale())); ((DecimalFormat) format).applyPattern(formatter.getFormat()); return (value, generator, context) -> generator.write(format.format(value)); } @Override void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context) { actualSerializer.serialize(value, generator, context); } abstract void writeValue(T value, JsonGenerator generator); } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/BigDecimalSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.math.BigDecimal; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link BigDecimal} type. */ class BigDecimalSerializer extends AbstractNumberSerializer { BigDecimalSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(BigDecimal value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/BigIntegerSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.math.BigInteger; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link BigInteger} type. */ class BigIntegerSerializer extends AbstractNumberSerializer { BigIntegerSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(BigInteger value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/BooleanSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Boolean} type. */ class BooleanSerializer extends TypeSerializer { BooleanSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Boolean value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ByteSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Byte} type. */ class ByteSerializer extends AbstractNumberSerializer { ByteSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Byte value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/CalendarSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Locale; /** * Serializer of the {@link Calendar} type. */ class CalendarSerializer extends AbstractDateSerializer { CalendarSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(Calendar value) { return value.toInstant(); } @Override protected String formatDefault(Calendar value, Locale locale) { DateTimeFormatter formatter = value.isSet(Calendar.HOUR) || value.isSet(Calendar.HOUR_OF_DAY) ? DateTimeFormatter.ISO_DATE_TIME : DateTimeFormatter.ISO_DATE; return formatter.withZone(value.getTimeZone().toZoneId()) .withLocale(locale).format(toTemporalAccessor(value)); } @Override protected TemporalAccessor toTemporalAccessor(Calendar object) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(object.getTimeInMillis()), object.getTimeZone().toZoneId()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/CharSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Character} type. */ class CharSerializer extends TypeSerializer { CharSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Character value, JsonGenerator generator, SerializationContextImpl context) { generator.write(String.valueOf(value)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/DateSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Date; import java.util.Locale; import org.eclipse.yasson.internal.JsonbDateFormatter; /** * Serializer of the {@link Date} type. */ class DateSerializer extends AbstractDateSerializer { private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC); DateSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(Date value) { return value.toInstant(); } @Override protected String formatDefault(Date value, Locale locale) { return DEFAULT_DATE_FORMATTER.withLocale(locale).format(toInstant(value)); } @Override protected String formatWithFormatter(Date value, DateTimeFormatter formatter) { return getZonedFormatter(formatter).format(toTemporalAccessor(value)); } @Override protected String formatStrictIJson(Date value) { return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(toTemporalAccessor(value)); } @Override protected TemporalAccessor toTemporalAccessor(Date object) { return toInstant(object); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/DoubleSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Double} type. */ class DoubleSerializer extends AbstractNumberSerializer { DoubleSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Double value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/DurationSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Duration; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Duration} type. */ class DurationSerializer extends TypeSerializer { DurationSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Duration value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Enum} types. */ class EnumSerializer extends TypeSerializer> { EnumSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Enum value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.name()); } @Override void serializeKey(Enum key, JsonGenerator generator, SerializationContextImpl context) { generator.writeKey(key.name()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/FloatSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.math.BigDecimal; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Float} type. */ class FloatSerializer extends AbstractNumberSerializer { FloatSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Float value, JsonGenerator generator) { //floats lose precision, after upcasting to doubles in jsonp generator.write(new BigDecimal(String.valueOf(value))); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/InstantSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.Locale; import org.eclipse.yasson.internal.JsonbDateFormatter; /** * Serializer of the {@link Instant} type. */ class InstantSerializer extends AbstractDateSerializer { InstantSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(Instant value) { return value; } @Override protected String formatDefault(Instant value, Locale locale) { return DateTimeFormatter.ISO_INSTANT.withLocale(locale).format(value); } @Override protected String formatWithFormatter(Instant value, DateTimeFormatter formatter) { return formatter.withZone(UTC).format(value); } @Override protected String formatStrictIJson(Instant value) { return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/IntegerSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Integer} type. */ class IntegerSerializer extends AbstractNumberSerializer { IntegerSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Integer value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/JsonValueSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.JsonValue; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link JsonValue} type. */ class JsonValueSerializer extends TypeSerializer { JsonValueSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(JsonValue value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.LocalDate; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import org.eclipse.yasson.internal.JsonbDateFormatter; /** * Serializer of the {@link LocalDate} type. */ class LocalDateSerializer extends AbstractDateSerializer { private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE.withZone(UTC); LocalDateSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(LocalDate value) { return Instant.from(value.atStartOfDay(UTC)); } @Override protected String formatDefault(LocalDate value, Locale locale) { return DEFAULT_FORMAT.withLocale(locale).format(value); } @Override protected String formatStrictIJson(LocalDate value) { final ZonedDateTime zonedDateTime = value.atTime(0, 0, 0).atZone(UTC); return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(zonedDateTime); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateTimeSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import org.eclipse.yasson.internal.JsonbDateFormatter; /** * Serializer of the {@link LocalDateTime} type. */ class LocalDateTimeSerializer extends AbstractDateSerializer { LocalDateTimeSerializer(TypeSerializerBuilder builder) { super(builder); } @Override protected Instant toInstant(LocalDateTime value) { return value.atZone(UTC).toInstant(); } @Override protected String formatDefault(LocalDateTime value, Locale locale) { return DateTimeFormatter.ISO_LOCAL_DATE_TIME.withLocale(locale).format(value); } @Override protected String formatWithFormatter(LocalDateTime value, DateTimeFormatter formatter) { return getZonedFormatter(formatter).format(value); } @Override protected String formatStrictIJson(LocalDateTime value) { final ZonedDateTime zonedDateTime = value.atZone(UTC); return JsonbDateFormatter.IJSON_DATE_FORMATTER.format(zonedDateTime); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/LocalTimeSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Serializer of the {@link LocalTime} type. */ class LocalTimeSerializer extends AbstractDateSerializer { LocalTimeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(LocalTime value) { throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, LocalTime.class.getSimpleName())); } @Override protected String formatDefault(LocalTime value, Locale locale) { return DateTimeFormatter.ISO_LOCAL_TIME.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/LongSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Long} type. */ class LongSerializer extends AbstractNumberSerializer { LongSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Long value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/MonthDayTypeSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.MonthDay; import java.time.Year; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Serializer of the {@link MonthDay} type. */ class MonthDayTypeSerializer extends AbstractDateSerializer { private static final int YEAR_NUMBER = Year.now().getValue(); private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("--MM-dd").withZone(UTC); MonthDayTypeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(MonthDay value) { return value.atYear(YEAR_NUMBER).atStartOfDay(UTC).toInstant(); } @Override protected String formatDefault(MonthDay value, Locale locale) { return DEFAULT_FORMAT.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/NumberSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.math.BigDecimal; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Number} type. */ class NumberSerializer extends AbstractNumberSerializer { NumberSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Number value, JsonGenerator generator) { generator.write(new BigDecimal(String.valueOf(value))); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ObjectTypeSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.lang.reflect.Type; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.serializer.ModelSerializer; import org.eclipse.yasson.internal.serializer.SerializationModelCreator; /** * Object type serializer. Dynamically resolves the serialized type based on the serialized instance class. */ public class ObjectTypeSerializer extends TypeSerializer { private final Customization customization; private final Map, ModelSerializer> cache; private final List chain; private final boolean isKey; ObjectTypeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); this.customization = serializerBuilder.getCustomization(); this.cache = new ConcurrentHashMap<>(); this.chain = new LinkedList<>(serializerBuilder.getChain()); this.isKey = serializerBuilder.isKey(); } @Override void serializeValue(Object value, JsonGenerator generator, SerializationContextImpl context) { //Dynamically resolved type during runtime. Cached in SerializationModelCreator. findSerializer(value, generator, context); } @Override void serializeKey(Object key, JsonGenerator generator, SerializationContextImpl context) { if (key == null) { super.serializeKey(null, generator, context); return; } //Dynamically resolved type during runtime. Cached in SerializationModelCreator. findSerializer(key, generator, context); } private void findSerializer(Object key, JsonGenerator generator, SerializationContextImpl context) { Class clazz = key.getClass(); cache.computeIfAbsent(clazz, aClass -> { SerializationModelCreator serializationModelCreator = context.getJsonbContext().getSerializationModelCreator(); return serializationModelCreator.serializerChainRuntime(new LinkedList<>(chain), clazz, customization, false, isKey); }).serialize(key, generator, context); } /** * Add serializer to the cache. * * @param clazz class of the serializer * @param modelSerializer model serializer bound to the class */ public void addSpecificSerializer(Class clazz, ModelSerializer modelSerializer) { cache.put(clazz, modelSerializer); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetDateTimeSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Serializer of the {@link OffsetDateTime} type. */ class OffsetDateTimeSerializer extends AbstractDateSerializer { OffsetDateTimeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(OffsetDateTime value) { return value.toInstant(); } @Override protected String formatDefault(OffsetDateTime value, Locale locale) { return DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetTimeSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.OffsetTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Serializer of the {@link OffsetTime} type. */ class OffsetTimeSerializer extends AbstractDateSerializer { OffsetTimeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(OffsetTime value) { throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, OffsetTime.class.getSimpleName())); } @Override protected String formatDefault(OffsetTime value, Locale locale) { return DateTimeFormatter.ISO_OFFSET_TIME.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalDoubleSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.util.OptionalDouble; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * Serializer of the {@link OptionalDouble} type. */ class OptionalDoubleSerializer implements ModelSerializer { private final ModelSerializer typeSerializer; OptionalDoubleSerializer(ModelSerializer typeSerializer) { this.typeSerializer = typeSerializer; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { OptionalDouble optionalDouble = (OptionalDouble) value; if (optionalDouble != null && optionalDouble.isPresent()) { typeSerializer.serialize(optionalDouble.getAsDouble(), generator, context); } else { typeSerializer.serialize(null, generator, context); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalIntSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.util.OptionalInt; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * Serializer of the {@link OptionalInt} type. */ class OptionalIntSerializer implements ModelSerializer { private final ModelSerializer typeSerializer; OptionalIntSerializer(ModelSerializer typeSerializer) { this.typeSerializer = typeSerializer; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { OptionalInt optionalInt = (OptionalInt) value; if (optionalInt != null && optionalInt.isPresent()) { typeSerializer.serialize(optionalInt.getAsInt(), generator, context); } else { typeSerializer.serialize(null, generator, context); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalLongSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.util.OptionalLong; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * Serializer of the {@link OptionalLong} type. */ class OptionalLongSerializer implements ModelSerializer { private final ModelSerializer typeSerializer; OptionalLongSerializer(ModelSerializer typeSerializer) { this.typeSerializer = typeSerializer; } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { OptionalLong optionalLong = (OptionalLong) value; if (optionalLong != null && optionalLong.isPresent()) { typeSerializer.serialize(optionalLong.getAsLong(), generator, context); } else { typeSerializer.serialize(null, generator, context); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/PathSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.nio.file.Path; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Path} type. */ class PathSerializer extends TypeSerializer { PathSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Path value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/PeriodSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Period; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link Period} type. */ class PeriodSerializer extends TypeSerializer { PeriodSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(Period value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ShortSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; /** * Serializer of the {@link Short} type. */ class ShortSerializer extends AbstractNumberSerializer { ShortSerializer(TypeSerializerBuilder builder) { super(builder); } @Override void writeValue(Short value, JsonGenerator generator) { generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/SqlDateSerializer.java ================================================ /* * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Locale; /** * Common serializer for {@link Date} and {@link java.sql.Date} types. */ class SqlDateSerializer extends DateSerializer { SqlDateSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(Date value) { if (value instanceof java.sql.Date) { // java.sql.Date doesn't have a time component, so do our best if TIME_IN_MILLIS is requested // In the future (at a breaking change boundary) we should probably reject this code path return Instant.ofEpochMilli(value.getTime()); } else { return super.toInstant(value); } } @Override protected String formatDefault(Date value, Locale locale) { if (value instanceof java.sql.Date) { return value.toString() + 'Z'; // Z is the UTC timezone indicator } else { return super.formatDefault(value, locale); } } @Override protected String formatWithFormatter(Date value, DateTimeFormatter formatter) { if (value instanceof java.sql.Date) { return ((java.sql.Date) value).toLocalDate().format(formatter); } else { return super.formatWithFormatter(value, formatter); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/SqlTimestampSerializer.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.sql.Timestamp; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Serializer of the {@link Timestamp} type. */ class SqlTimestampSerializer extends AbstractDateSerializer { /** * Default Yasson {@link DateTimeFormatter}. */ private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC); SqlTimestampSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(Timestamp value) { return value.toInstant(); } @Override protected String formatDefault(Timestamp value, Locale locale) { return DEFAULT_FORMATTER.withLocale(locale).format(toInstant(value)); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/StringSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.nio.charset.StandardCharsets; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.JsonbConfigProperties; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Serializer of the {@link String} type. */ class StringSerializer extends TypeSerializer { StringSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(String value, JsonGenerator generator, SerializationContextImpl context) { JsonbConfigProperties configProperties = context.getJsonbContext().getConfigProperties(); if (configProperties.isStrictIJson()) { String newString = new String(value.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); if (!newString.equals(value)) { throw new JsonbException(Messages.getMessage(MessageKeys.UNPAIRED_SURROGATE)); } } generator.write(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/TimeZoneSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.util.TimeZone; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link TimeZone} type. */ class TimeZoneSerializer extends TypeSerializer { TimeZoneSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(TimeZone value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.getID()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; import org.eclipse.yasson.internal.serializer.ModelSerializer; /** * Base for all the type serializers. */ abstract class TypeSerializer implements ModelSerializer { private final ModelSerializer serializer; TypeSerializer(TypeSerializerBuilder serializerBuilder) { if (serializerBuilder.isKey()) { serializer = new KeySerializer(); } else { serializer = new ValueSerializer(); } } @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { serializer.serialize(value, generator, context); } abstract void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context); void serializeKey(T key, JsonGenerator generator, SerializationContextImpl context) { generator.writeKey(String.valueOf(key)); } private final class ValueSerializer implements ModelSerializer { @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { serializeValue((T) value, generator, context); } } private final class KeySerializer implements ModelSerializer { @SuppressWarnings("unchecked") @Override public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) { serializeKey((T) value, generator, context); } } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializerBuilder.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.lang.reflect.Type; import java.util.List; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.model.customization.Customization; /** * Type serializer data holder object used during serializer creation. */ class TypeSerializerBuilder { private final List chain; private final Class clazz; private final Customization customization; private final JsonbContext jsonbContext; private final boolean key; TypeSerializerBuilder(List chain, Class clazz, Customization customization, JsonbContext jsonbContext, boolean key) { this.chain = chain; this.clazz = clazz; this.customization = customization; this.jsonbContext = jsonbContext; this.key = key; } public List getChain() { return chain; } public Class getClazz() { return clazz; } public Customization getCustomization() { return customization; } public JsonbContext getJsonbContext() { return jsonbContext; } public boolean isKey() { return key; } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializers.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; import java.util.TimeZone; import java.util.UUID; import java.util.function.Function; import javax.xml.datatype.XMLGregorianCalendar; import jakarta.json.JsonNumber; import jakarta.json.JsonString; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbException; import jakarta.json.bind.serializer.JsonbSerializer; import org.eclipse.yasson.internal.JsonbContext; import org.eclipse.yasson.internal.model.customization.Customization; import org.eclipse.yasson.internal.serializer.ModelSerializer; import org.eclipse.yasson.internal.serializer.SerializationModelCreator; import static org.eclipse.yasson.internal.BuiltInTypes.isClassAvailable; /** * Specific type serializers. */ public class TypeSerializers { private static final Map, Function> SERIALIZERS; private static final Set> SUPPORTED_MAP_KEYS; private static final Map, Class> OPTIONALS; static { Map, Function> cache = new HashMap<>(); cache.put(Byte.class, ByteSerializer::new); cache.put(Byte.TYPE, ByteSerializer::new); cache.put(BigDecimal.class, BigDecimalSerializer::new); cache.put(BigInteger.class, BigIntegerSerializer::new); cache.put(Boolean.class, BooleanSerializer::new); cache.put(Boolean.TYPE, BooleanSerializer::new); cache.put(Calendar.class, CalendarSerializer::new); cache.put(Character.class, CharSerializer::new); cache.put(Character.TYPE, CharSerializer::new); cache.put(Date.class, DateSerializer::new); cache.put(Double.class, DoubleSerializer::new); cache.put(Double.TYPE, DoubleSerializer::new); cache.put(Duration.class, DurationSerializer::new); cache.put(Float.class, FloatSerializer::new); cache.put(Float.TYPE, FloatSerializer::new); cache.put(Integer.class, IntegerSerializer::new); cache.put(Integer.TYPE, IntegerSerializer::new); cache.put(Instant.class, InstantSerializer::new); cache.put(LocalDateTime.class, LocalDateTimeSerializer::new); cache.put(LocalDate.class, LocalDateSerializer::new); cache.put(LocalTime.class, LocalTimeSerializer::new); cache.put(Long.class, LongSerializer::new); cache.put(Long.TYPE, LongSerializer::new); cache.put(MonthDay.class, MonthDayTypeSerializer::new); cache.put(Number.class, NumberSerializer::new); cache.put(Object.class, ObjectTypeSerializer::new); cache.put(OffsetDateTime.class, OffsetDateTimeSerializer::new); cache.put(OffsetTime.class, OffsetTimeSerializer::new); cache.put(Path.class, PathSerializer::new); cache.put(Period.class, PeriodSerializer::new); cache.put(Short.class, ShortSerializer::new); cache.put(Short.TYPE, ShortSerializer::new); cache.put(String.class, StringSerializer::new); cache.put(TimeZone.class, TimeZoneSerializer::new); cache.put(URI.class, UriSerializer::new); cache.put(URL.class, UrlSerializer::new); cache.put(UUID.class, UuidSerializer::new); if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) { cache.put(XMLGregorianCalendar.class, XmlGregorianCalendarSerializer::new); } cache.put(YearMonth.class, YearMonthTypeSerializer::new); cache.put(ZonedDateTime.class, ZonedDateTimeSerializer::new); cache.put(ZoneId.class, ZoneIdSerializer::new); cache.put(ZoneOffset.class, ZoneOffsetSerializer::new); if (isClassAvailable("java.sql.Date")) { cache.put(Date.class, SqlDateSerializer::new); cache.put(java.sql.Date.class, SqlDateSerializer::new); cache.put(java.sql.Timestamp.class, SqlTimestampSerializer::new); } SERIALIZERS = Map.copyOf(cache); Map, Class> optionals = new HashMap<>(); optionals.put(OptionalDouble.class, Double.class); optionals.put(OptionalInt.class, Integer.class); optionals.put(OptionalLong.class, Long.class); OPTIONALS = Map.copyOf(optionals); Set> mapKeys = new HashSet<>(SERIALIZERS.keySet()); mapKeys.addAll(optionals.keySet()); mapKeys.add(JsonNumber.class); mapKeys.add(JsonString.class); mapKeys.remove(Object.class); SUPPORTED_MAP_KEYS = Set.copyOf(mapKeys); } private TypeSerializers() { throw new IllegalStateException("Util class cannot be instantiated"); } /** * Whether type is the supported key type. * * @param clazz key type * @return whether type is supported key type */ public static boolean isSupportedMapKey(Class clazz) { return Enum.class.isAssignableFrom(clazz) || SUPPORTED_MAP_KEYS.contains(clazz); } /** * Whether type has a custom {@link JsonbSerializer} implementation. * * @param clazz type to serialize * @param jsonbContext jsonb context * @return whether a custom JsonSerializer for the type is available */ public static boolean hasCustomJsonbSerializer(Class clazz, JsonbContext jsonbContext) { return jsonbContext.getComponentMatcher().getSerializerBinding(clazz, null).isPresent(); } /** * Create new type serializer. * * @param clazz type of the serializer * @param customization serializer customization * @param jsonbContext jsonb context * @return new type serializer */ public static ModelSerializer getTypeSerializer(Class clazz, Customization customization, JsonbContext jsonbContext) { return getTypeSerializer(Collections.emptyList(), clazz, customization, jsonbContext, false); } /** * Create new type serializer. * * @param chain chain of the type predecessors * @param clazz type of the serializer * @param customization serializer customization * @param jsonbContext jsonb context * @param key whether serializer is a key * @return new type serializer */ public static ModelSerializer getTypeSerializer(List chain, Class clazz, Customization customization, JsonbContext jsonbContext, boolean key) { Class current = clazz; List chainClone = new LinkedList<>(chain); TypeSerializerBuilder builder = new TypeSerializerBuilder(chainClone, clazz, customization, jsonbContext, key); ModelSerializer typeSerializer = null; if (Object.class.equals(current)) { return SERIALIZERS.get(current).apply(builder); } if (OPTIONALS.containsKey(current)) { Class optionalInner = OPTIONALS.get(current); ModelSerializer serializer = getTypeSerializer(chainClone, optionalInner, customization, jsonbContext, key); if (OptionalInt.class.equals(current)) { return new OptionalIntSerializer(serializer); } else if (OptionalLong.class.equals(current)) { return new OptionalLongSerializer(serializer); } else if (OptionalDouble.class.equals(current)) { return new OptionalDoubleSerializer(serializer); } else { throw new JsonbException("Unsupported Optional type for serialization: " + clazz); } } if (Enum.class.isAssignableFrom(clazz)) { typeSerializer = new EnumSerializer(builder); } else if (JsonValue.class.isAssignableFrom(clazz)) { typeSerializer = new JsonValueSerializer(builder); } if (typeSerializer == null) { do { if (SERIALIZERS.containsKey(current)) { typeSerializer = SERIALIZERS.get(current).apply(builder); break; } current = current.getSuperclass(); } while (!Object.class.equals(current) && current != null); } if (key) { //We do not want any other special serializers around our type serializer if it will be used as a key return typeSerializer; } return typeSerializer == null ? null : SerializationModelCreator.wrapInCommonSet(typeSerializer, customization, jsonbContext); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/UriSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.net.URI; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link URI} type. */ class UriSerializer extends TypeSerializer { UriSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(URI value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/UrlSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.net.URL; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link URL} type. */ class UrlSerializer extends TypeSerializer { UrlSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(URL value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/UuidSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.util.UUID; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link UUID} type. */ class UuidSerializer extends TypeSerializer { UuidSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(UUID value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.toString()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/XmlGregorianCalendarSerializer.java ================================================ /* * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Locale; import javax.xml.datatype.XMLGregorianCalendar; /** * Serializer of the {@link XMLGregorianCalendar} type. */ class XmlGregorianCalendarSerializer extends AbstractDateSerializer { XmlGregorianCalendarSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(XMLGregorianCalendar value) { return Instant.ofEpochMilli(value.toGregorianCalendar().getTimeInMillis()); } @Override protected String formatDefault(XMLGregorianCalendar value, Locale locale) { DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; return formatter .withLocale(locale) .withZone(value.toGregorianCalendar().getTimeZone().toZoneId()) .format(toTemporalAccessor(value)); } @Override protected TemporalAccessor toTemporalAccessor(XMLGregorianCalendar object) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(object.toGregorianCalendar().getTimeInMillis()), object.toGregorianCalendar().getTimeZone().toZoneId()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/YearMonthTypeSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Serializer of the {@link YearMonth} type. */ class YearMonthTypeSerializer extends AbstractDateSerializer { private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM").withZone(UTC); YearMonthTypeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(YearMonth value) { return value.atDay(1).atStartOfDay(UTC).toInstant(); } @Override protected String formatDefault(YearMonth value, Locale locale) { return DEFAULT_FORMAT.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneIdSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.ZoneId; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link ZoneId} type. */ class ZoneIdSerializer extends TypeSerializer { ZoneIdSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(ZoneId value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.getId()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneOffsetSerializer.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.ZoneOffset; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.internal.SerializationContextImpl; /** * Serializer of the {@link ZoneOffset} type. */ class ZoneOffsetSerializer extends TypeSerializer { ZoneOffsetSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override void serializeValue(ZoneOffset value, JsonGenerator generator, SerializationContextImpl context) { generator.write(value.getId()); } } ================================================ FILE: src/main/java/org/eclipse/yasson/internal/serializer/types/ZonedDateTimeSerializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer.types; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * Serializer of the {@link ZonedDateTime} type. */ class ZonedDateTimeSerializer extends AbstractDateSerializer { ZonedDateTimeSerializer(TypeSerializerBuilder serializerBuilder) { super(serializerBuilder); } @Override protected Instant toInstant(ZonedDateTime value) { return value.toInstant(); } @Override protected String formatDefault(ZonedDateTime value, Locale locale) { return DateTimeFormatter.ISO_ZONED_DATE_TIME.withLocale(locale).format(value); } } ================================================ FILE: src/main/java/org/eclipse/yasson/spi/JsonbComponentInstanceCreator.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.spi; import java.io.Closeable; /** * Creates instances of JsonbComponents such as JsonbAdapter. * *

* Yasson attempts to load the implementations using {@link java.util.ServiceLoader} first. If there are multiple * implementations found the service provider with the highest priority is used. If there are no service providers found the * default implementation is used. *

*/ public interface JsonbComponentInstanceCreator extends Closeable { /** * Default component priority. */ int DEFAULT_PRIORITY = 0; /** * Returns instance of JsonbComponent for desired class. * * @param Jsonb component type * @param componentClass component class * @return component instance */ T getOrCreateComponent(Class componentClass); /** * @return the priority of the component */ default int getPriority() { return DEFAULT_PRIORITY; } } ================================================ FILE: src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java ================================================ /* * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Map; import java.util.Optional; import jakarta.json.bind.JsonbException; import jakarta.json.bind.config.PropertyNamingStrategy; import org.eclipse.yasson.internal.model.JsonbCreator; import org.eclipse.yasson.internal.model.Property; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; /** * Search for instance creator from other sources. * Mainly intended to add extensibility for different java versions and new features. */ public class ClassMultiReleaseExtension { private ClassMultiReleaseExtension() { throw new IllegalStateException("This class cannot be instantiated"); } static boolean shouldTransformToPropertyName(Method method) { return !method.getDeclaringClass().isRecord(); } static boolean isSpecialAccessorMethod(Method method, Map classProperties) { return isRecord(method.getDeclaringClass()) && method.getParameterCount() == 0 && !void.class.equals(method.getReturnType()) && classProperties.containsKey(method.getName()); } static JsonbCreator findCreator(Class clazz, Constructor[] declaredConstructors, AnnotationIntrospector introspector, PropertyNamingStrategy propertyNamingStrategy) { if (clazz.isRecord()) { if (declaredConstructors.length == 1) { return introspector.createJsonbCreator(declaredConstructors[0], null, clazz, propertyNamingStrategy); } } return null; } public static boolean isRecord(Class clazz) { return clazz.isRecord(); } public static Optional exceptionToThrow(Class clazz) { if (clazz.isRecord()) { if (clazz.getDeclaredConstructors().length > 1) { return Optional.of(new JsonbException(Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, clazz))); } } return Optional.empty(); } } ================================================ FILE: src/main/resources/META-INF/native-image/org.eclipse/yasson/native-image.properties ================================================ # # Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # # This adds command line options to native image tool Args=-H:IncludeResourceBundles=yasson-messages ================================================ FILE: src/main/resources/META-INF/services/jakarta.json.bind.spi.JsonbProvider ================================================ org.eclipse.yasson.JsonBindingProvider ================================================ FILE: src/main/resources/yasson-messages.properties ================================================ # # Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # # The following 2 comment lines are for automated tools that the IBM translation teams use #NLS_MESSAGEFORMAT_VAR #NLS_ENCODING=UNICODE cannotMarshallObject=Cannot marshall object typeResolutionError=Error resolving runtime type for type: {0} genericBoundNotFound=Generic bound not found for type {0} declared in {1}. cantConvertJsonValue=Cannot convert JSON value into: {0} cantCreateRootInstance=Cannot create root instance invokingGetter=Error invoking getter method. gettingValue=Error getting field value. settingValue=Error setting field value. noLoggerName=Logger name has to be set resolveParametrizedType=Cannot resolve ParameterizedType superclass for: {0} cantCreateInstance=Cannot create instance inferTypeForUnmarshall=Cannot infer a type for unmarshalling into: {0} implClassIncompatible=Implementation class {0} is not a subtype of interface {1}. notValueType=Not a value type: {0} unexpectedParseEvent=Unexpected parser event: {0} processFromJson=Process class: {0} from json using converter: {1} processToJson=Process class: {0} to json using converter: {1} creatingHandles=Error creating handles. settingValueWith=Error setting value with: {0} gettingValueWith=Error getting value with: {0} unpairedSurrogate=String contains unpaired surrogate. adapterException=Problem adapting object of type {0} to {1} in class {2} adapterFound=Found adapter from type {0} to type {1}. adapterIncompatible=Adapter of runtime type {0} does not match property type {1} propertyOrder=Property order strategy with name {0} was not recognized unknownVisibilityStrategy=Property visibility strategy with name {0} was not recognized unsupportedJsonpSerializerValue=Unsupported value of type {0} for JSON serializer. noJndiEnvironment=No JNDI environment ({0}) found to look for CDI provider. noCdiApiProvider=CDI API not found on class or module path {0}. illegalAccess=Yasson does not have sufficient permissions to {0}. usingDefaultConstructorInstantiator=CDI bean manager not found, serializers and adapters will not have CDI support. noCdiEnvironment=CDI environment is not available. iJsonEnabledSingleValue=Cannot marshal single value because I-Json is enabled! dateTypeNotSupported=Date type not supported: {0} errorParsingDate=Error parsing {1} from value: {0}. Check your @JsonbDateFormat has all time units for {1} type, \ or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING. noDefaultConstructor=Cannot create instance of a class: {0}, No default constructor found. recordMultipleConstructors=Cannot create instance of a record: {0}, Multiple constructors found. offsetDateTimeFromMillis=Parsing {0} from epoch millisecond, UTC zone offset will be used. timeToEpochMillisError=Cannot convert {0} to/from epoch milliseconds. configPropertyInvalidType=JsonbConfig property [{0}] must be of type [{1}]. conversionNotSupported=Conversion of type [{0}] to/from string value is not supported. endOfJsonStructure=End of JSON structure reached. invalidDeserializationType=Invalid json value type {0} for deserialization into type {1}. czechDiacritics=p\u0159\u00edli\u0161,\u017elu\u0165ou\u010dk\u00fd,k\u016f\u0148,\u00fap\u011bl,\u010f\u00e1belsk\u00e9,\u00f3dy russianDiacritics=\u0441\u043b\u0438\u0448\u043a\u043e\u043c,\u0436\u0435\u043b\u0442\u0435\u043d\u044c\u043a\u0438\u0439,\u043a\u043e\u043d\u044c,\u043f\u0435\u043b,\u0430\u0434\u0441\u043a\u0438\u0435,\u043e\u0434\u044b propertyNotFoundDeserializer=Property {0} was not found in class {1}. Skipping value {2}. settingPropertyDeserializer=Setting property {0} in class {1} with value {2}. classLoadNotAllowed=Class load for {0} name is not allowed! errorCallingJsonbCreator=Exception occurred during call to JSONB creator on class: {0}. incompatibleFactoryCreatorReturnType=Return type of creator {0} must be of type {1}. multipleJsonbCreators=More than one @JsonbCreator declared in class {0}. internalError=Internal error: {0} serializePropertyError=Unable to serialize property ''{0}'' from {1} deserializeValueError=Error deserialize JSON value into type: {0}. parsingNumber=Error parsing number {0} with format {1}. unknownBinaryDataStrategy=Unknown binary data strategy: {0} invalidPropertyNamingStrategy=Property naming strategy must be either: String name, or instance of jakarta.json.bind.config.PropertyNamingStrategy. creatorParameterNotAnnotated=Argument has no JsonbProperty annotation for JsonbCreator {0}. unknownJsonProperty=Json property {0} can not be mapped to a class {1}. jsonbCreatorMissingProperty=JsonbCreator parameter {0} is missing in json document. zoneParseError=Cannot parse zone from json value: {0} jsonbTransientWithOtherAnnotations=JsonbTransient annotation cannot be used with other jsonb annotations on the same property. nonParametrizedType=Type: {0} is not a parametrized type. propertyNameClash=Property {0} clashes with property {1} by read or write name in class {2}. sqlDateIJsonError=java.sql.Date is not supported in STRICT_IJSON mode. recursiveReference=Recursive reference has been found in class {0}. datatypeFactoryCreationFailed=An error occurred while DatatypeFactory creation. multipleConstructorPropertiesCreators=More than one constructor annotated with @ConstructorProperties declared in class {0}. annotationNotAvailable=Annotation {0} is not visible in modules or classpath. Annotation will be ignored. missingValuePropertyInAnnotation=Missing value property in Annotation {0}. Annotation will be ignored. numberIncompatibleValueTypeArray=Value type {0} is not a JsonNumber. numberIncompatibleValueTypeObject=Value type {0} at key {1} is not a JsonNumber. ================================================ FILE: src/test/java/PackagelessClassTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; /** * Tests the serialization/deserialization of a class that has no package. * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class PackagelessClassTest { @Test public void testSerialization() throws Exception { PackagelessModel packagelessClass = new PackagelessModel(12, "Hello World!"); String expected = "{\"intValue\":12,\"stringValue\":\"Hello World!\"}"; assertEquals(expected, defaultJsonb.toJson(packagelessClass)); } @Test public void testDeSerialization() throws Exception { PackagelessModel packagelessClass = new PackagelessModel(12, "Hello World!"); String input = "{\"intValue\":12,\"stringValue\":\"Hello World!\"}"; PackagelessModel packagelessModel = defaultJsonb.fromJson(input, PackagelessModel.class); assertEquals(packagelessModel.getIntValue(), 12); assertEquals(packagelessModel.getStringValue(), "Hello World!"); } } ================================================ FILE: src/test/java/PackagelessModel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /** * This class is designed outside any packages intentionally to test serialization/deserialization scenarios for such * cases. * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class PackagelessModel { private int intValue; private String stringValue; public PackagelessModel() { } public PackagelessModel(int intValue, String stringValue) { this.intValue = intValue; this.stringValue = stringValue; } public int getIntValue() { return intValue; } public void setIntValue(int intValue) { this.intValue = intValue; } public String getStringValue() { return stringValue; } public void setStringValue(String stringValue) { this.stringValue = stringValue; } } ================================================ FILE: src/test/java/org/eclipse/yasson/Assertions.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import static org.junit.jupiter.api.Assertions.*; import java.util.function.Function; import java.util.function.Supplier; import jakarta.json.bind.JsonbException; public class Assertions { /** * Asserts that the given operation will fail with a JsonbException * @param operation The operation that is expected to fail */ public static void shouldFail(Supplier operation) { shouldFail(operation, JsonbException.class, msg -> true); } public static void shouldFail(Runnable operation) { shouldFail(() -> { operation.run(); return null; }); } /** * Asserts that the given operation will fail with a JsonbException * @param operation The operation that is expected to fail * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception * includes a specific token. */ public static void shouldFail(Supplier operation, Function checkExceptionMessage) { shouldFail(operation, JsonbException.class, checkExceptionMessage); } /** * Asserts that the given operation will fail * @param operation The operation that is expected to fail * @param expectedType The expected exception type to receive when evaluating the operation * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception * includes a specific token. */ public static void shouldFail(Supplier operation, Class expectedType, Function checkExceptionMessage) { try { operation.get(); fail("The operation should have failed with a " + expectedType.getCanonicalName() + " but it succeeded."); } catch (Throwable t) { String fullErrorMessage = ""; for (Throwable current = t; current != null && current.getCause() != current; current = current.getCause()) { fullErrorMessage += current.getClass().getCanonicalName() + ": "; fullErrorMessage += current.getMessage() + "\n"; } if (expectedType.isAssignableFrom(t.getClass())) { if (!checkExceptionMessage.apply(fullErrorMessage)) { t.printStackTrace(); fail("Exception did not contain the proper content: " + fullErrorMessage); } } else { t.printStackTrace(); fail("Expected to get an exception of " + expectedType + " but instead was " + t.getClass()); } } } } ================================================ FILE: src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.internal.JsonbContext; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.spi.JsonProvider; import java.lang.reflect.Method; /** * * @author Maxence Laurent */ public class DefaultGetterInInterface { public static interface Defaulted { default public String getGetterA() { return "valueA"; } } public static class PojoWithDefault implements Defaulted { } @Test public void testWithDefault() { PojoWithDefault pojo = new PojoWithDefault(); String result = defaultJsonb.toJson(pojo); assertEquals("{\"getterA\":\"valueA\"}", result); } public static interface WithGetterI { @JsonbProperty("withGetterI") String getGetterI(); } public static interface WithDefaultGetterI extends WithGetterI { @Override @JsonbProperty("default") default String getGetterI() { return "default"; } } public static interface OtherWithDefaultGetterI extends WithGetterI { @Override @JsonbProperty("otherDefault") default String getGetterI() { return "otherDefault"; } } public static class Pojo implements WithGetterI { @Override @JsonbProperty("implementation") public String getGetterI() { return "implementation"; } } public static class PojoNoAnnotation implements WithGetterI { @Override public String getGetterI() { return "withGetterI"; } } public static class PojoWithDefaultSuperImplementation extends Pojo implements WithDefaultGetterI { } public static class PojoWithDefaultImplementation implements WithDefaultGetterI { @Override @JsonbProperty("defaultImplementation") public String getGetterI() { return "defaultImplementation"; } } public static class PojoWithDefaultOnly implements WithDefaultGetterI { } public static class PojoGetterDefaultedTwice extends PojoWithDefaultImplementation implements OtherWithDefaultGetterI { } @Test public void testWithInheritedAndDefault() throws NoSuchMethodException { JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider()); // direct implementation only (no default implementation) Method pojoGetter = jsonbContext.getMappingContext().getOrCreateClassModel(Pojo.class).getPropertyModel("getterI").getGetter(); // default implementation Method pojoDefaultOnlyGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultOnly.class).getPropertyModel("getterI").getGetter(); // default implementation overriden by super class Method pojoDefaultSuperGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultSuperImplementation.class).getPropertyModel("getterI").getGetter(); // default implementation Method pojoDefaultImplementationGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultImplementation.class).getPropertyModel("getterI").getGetter(); // two default implementations Method pojoTwoDefault = jsonbContext.getMappingContext().getOrCreateClassModel(PojoGetterDefaultedTwice.class).getPropertyModel("getterI").getGetter(); // assert getters selected by ClassParser assertEquals(Pojo.class.getMethod("getGetterI"), pojoGetter); assertEquals(WithDefaultGetterI.class.getMethod("getGetterI"), pojoDefaultOnlyGetter); assertEquals(Pojo.class.getMethod("getGetterI"), pojoDefaultSuperGetter); assertEquals(PojoWithDefaultImplementation.class.getMethod("getGetterI"), pojoDefaultImplementationGetter); assertEquals(PojoWithDefaultImplementation.class.getMethod("getGetterI"), pojoTwoDefault); // assert serialized json is correct, including property name as specified by JsonbProperty annotations assertJson(new Pojo(), "implementation"); assertJson(new PojoNoAnnotation(), "withGetterI"); assertJson(new PojoWithDefaultOnly(), "default"); assertJson(new PojoWithDefaultSuperImplementation(), "implementation"); assertJson(new PojoWithDefaultImplementation(), "defaultImplementation"); assertJson(new PojoGetterDefaultedTwice(), "defaultImplementation"); } private static void assertJson(WithGetterI pojo, String expected){ assertEquals(expected, pojo.getGetterI()); assertEquals("{\"" + expected + "\":\"" + pojo.getGetterI() + "\"}", defaultJsonb.toJson(pojo)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */package org.eclipse.yasson; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbTransient; import jakarta.json.bind.config.PropertyVisibilityStrategy; import java.lang.reflect.Field; import java.lang.reflect.Method; public class FieldAccessStrategyTest { public static class PrivateFields { private String strField; @JsonbTransient private boolean setterCalled; @JsonbTransient private boolean getterCalled; public PrivateFields() { } public PrivateFields(String strField) { this.strField = strField; } public String getStrField() { getterCalled = true; return strField; } public void setStrField(String strField) { setterCalled = true; this.strField = strField; } } public static class PublicFields { public String strField; } @Test public void testPrivateFields() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new FieldAccessStrategy())); PrivateFields pojo = new PrivateFields("pojo string"); String expected = "{\"strField\":\"pojo string\"}"; assertEquals(expected, jsonb.toJson(pojo)); PrivateFields result = jsonb.fromJson(expected, PrivateFields.class); assertEquals(false, result.getterCalled); assertEquals(false, result.setterCalled); assertEquals("pojo string", result.strField); } @Test public void testHidePublicFields() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new NoAccessStrategy())); PublicFields pojo = new PublicFields(); pojo.strField = "string field"; String expected = "{}"; assertEquals(expected, jsonb.toJson(pojo)); PublicFields result = jsonb.fromJson("{\"strField\":\"pojo string\"}", PublicFields.class); assertEquals(null, result.strField); } /** * Ignores public / private, visibility is set by name. */ @Test public void testCustomVisibityStrategy() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new CustomVisibilityStrategy())); String json = "{\"floatInstance\":10.0,\"stringInstance\":\"Test String\"}"; SimpleContainer simpleContainer = new SimpleContainer(); simpleContainer.setStringInstance("Test String"); simpleContainer.setIntegerInstance(10); simpleContainer.setFloatInstance(10.0f); assertEquals(json, jsonb.toJson(simpleContainer)); SimpleContainer result = jsonb.fromJson("{ \"stringInstance\" : \"Test String\", \"floatInstance\" : 1.0, \"integerInstance\" : 1 }", SimpleContainer.class); assertEquals("Test String", result.stringInstance); assertNull(result.integerInstance); assertNull(result.floatInstance); } public class CustomVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return field.getName().equals("stringInstance"); } @Override public boolean isVisible(Method method) { return method.getName().equals("getFloatInstance"); } } public static class SimpleContainer { private String stringInstance; private Integer integerInstance; private Float floatInstance; public String getStringInstance() { return stringInstance; } public void setStringInstance(String stringInstance) { this.stringInstance = stringInstance; } public Integer getIntegerInstance() { return integerInstance; } public void setIntegerInstance(Integer integerInstance) { this.integerInstance = integerInstance; } public float getFloatInstance() { return floatInstance; } public void setFloatInstance(float floatInstance) { this.floatInstance = floatInstance; } } private static final class NoAccessStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { return false; } @Override public boolean isVisible(Method method) { return false; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/Issue454Test.java ================================================ /* * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbTransient; public class Issue454Test { @Test public void test() { final String EXPECTED = "{\"field2\":\"bbb\"}"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); assertEquals(EXPECTED, jsonb.toJson(new TheInterface() { @Override public String getField1() { return "aaa"; } @Override public String getField2() { return "bbb"; }})); assertEquals(EXPECTED, jsonb.toJson(new TheClass() { @Override public String getField1() { return "aaa"; } @Override public String getField2() { return "bbb"; }})); assertEquals(EXPECTED, jsonb.toJson(new TheClass2())); assertEquals(EXPECTED, jsonb.toJson(new TheClass2() {})); } public static abstract class TheClass { @JsonbTransient public abstract String getField1(); public abstract String getField2(); } public static class TheClass2 extends TheClass { @Override public String getField1() { return "aaa"; } @Override public String getField2() { return "bbb"; } } public static interface TheInterface { @JsonbTransient String getField1(); String getField2(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/Issue456Test.java ================================================ /* * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbException; public class Issue456Test { @Test public void dontInvokeToString() { try { JsonbBuilder.create().toJson(new Example()); fail("JsonbException is expected"); } catch (JsonbException e) { // Expected } } public static class Example { public String getProperty() { throw new RuntimeException("some error"); } @Override public String toString() { return JsonbBuilder.create().toJson(this); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.internal.cdi.NonCdiAdapter; import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory; import jakarta.json.bind.annotation.JsonbTypeAdapter; /** * Requires --limit-modules java.base,java.logging,java.sql (to exclude java.naming) to work. * See pom.xml surefire plugin configuration. */ public class JavaxNamingExcludedTest { @Test public void testNoJavaxNamingModule() { try { Class.forName(JsonbComponentInstanceCreatorFactory.INITIAL_CONTEXT_CLASS); fail("Class [" + JsonbComponentInstanceCreatorFactory.INITIAL_CONTEXT_CLASS + "] should not be available for this test."); } catch (ClassNotFoundException e) { //OK, java.naming is not observable } final String result = defaultJsonb.toJson(new AdaptedPojo()); assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result); } public static final class AdaptedPojo { @JsonbTypeAdapter(NonCdiAdapter.class) public String adaptedValue1 = "1111"; @JsonbTypeAdapter(NonCdiAdapter.class) public String adaptedValue2 = "1001"; @JsonbTypeAdapter(NonCdiAdapter.class) public String adaptedValue3 = "1010"; } } ================================================ FILE: src/test/java/org/eclipse/yasson/Jsonbs.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import jakarta.json.bind.*; import org.eclipse.yasson.internal.*; public class Jsonbs { public static final Jsonb defaultJsonb = JsonbBuilder.create(); public static final Jsonb bindingJsonb = new JsonBindingBuilder().build(); public static final Jsonb formattingJsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(Boolean.TRUE)); public static final Jsonb nullableJsonb = JsonbBuilder.create(new JsonbConfig().withNullValues(Boolean.TRUE)); public static final YassonJsonb yassonJsonb = (YassonJsonb) JsonbBuilder.create(); public static final YassonJsonb bindingYassonJsonb = (YassonJsonb) new JsonBindingProvider().create().build(); } ================================================ FILE: src/test/java/org/eclipse/yasson/SimpleTest.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.bindingJsonb; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Roman Grigoriadi */ public class SimpleTest { @Test public void testSimpleSerialize() { final StringWrapper wrapper = new StringWrapper(); wrapper.setValue("abc"); final String val = bindingJsonb.toJson(wrapper); assertEquals("{\"value\":\"abc\"}", val); } @Test public void testSimpleDeserializer() { final StringWrapper stringWrapper = defaultJsonb.fromJson("{\"value\":\"abc\"}", StringWrapper.class); assertEquals("abc", stringWrapper.value); } public static class StringWrapper { public String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/TestTypeToken.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * @author Roman Grigoriadi */ public abstract class TestTypeToken { public Type getType() { return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } } ================================================ FILE: src/test/java/org/eclipse/yasson/YassonConfigTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson; import static org.junit.jupiter.api.Assertions.assertEquals; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import org.junit.jupiter.api.Test; /** * Tests that the names of configuration fields in {@link YassonConfig} do not change. */ public class YassonConfigTest { @SuppressWarnings("deprecation") @Test public void testFailOnUnknownPropertiesUnchanged() { assertEquals("jsonb.fail-on-unknown-properties", YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES); assertEquals("jsonb.fail-on-unknown-properties", YassonProperties.FAIL_ON_UNKNOWN_PROPERTIES); } @SuppressWarnings("deprecation") @Test public void testUserTypeMappingUnchanged() { assertEquals("jsonb.user-type-mapping", YassonConfig.USER_TYPE_MAPPING); assertEquals("jsonb.user-type-mapping", YassonProperties.USER_TYPE_MAPPING); } @SuppressWarnings("deprecation") @Test public void testZeroTimeDefaultingUnchanged() { assertEquals("jsonb.zero-time-defaulting", YassonConfig.ZERO_TIME_PARSE_DEFAULTING); assertEquals("jsonb.zero-time-defaulting", YassonProperties.ZERO_TIME_PARSE_DEFAULTING); } @SuppressWarnings("deprecation") @Test public void testNullRootSerializerUnchanged() { assertEquals("yasson.null-root-serializer", YassonConfig.NULL_ROOT_SERIALIZER); assertEquals("yasson.null-root-serializer", YassonProperties.NULL_ROOT_SERIALIZER); } @Test public void testEagerInitClassesUnchanged() { assertEquals("yasson.eager-parse-classes", YassonConfig.EAGER_PARSE_CLASSES); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.adapters.model.*; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import jakarta.json.Json; import jakarta.json.JsonObject; import jakarta.json.JsonString; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import static java.util.Collections.unmodifiableMap; /** * Tests adapters to behave correctly. * * @author Roman Grigoriadi */ public class AdaptersTest { public static class NonGenericPojo { public String strValues; public Box box; } @Test public void testBoxToCrateNoGenerics() throws Exception { JsonbAdapter[] adapters = { new JsonbAdapter() { @Override public Crate adaptToJson(Box box) { final Crate crate = new Crate(); crate.setCrateStrField("crateAdapted" + box.getBoxStrField()); crate.setCrateIntField(box.getBoxIntegerField() + 1); return crate; } @Override public Box adaptFromJson(Crate crate) { Box box = new Box(); box.setBoxStrField("boxAdapted" + crate.getCrateStrField()); box.setBoxIntegerField(crate.getCrateIntField() + 1); return box; } } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo(); Box box = new Box(); box.setBoxStrField("BoxStr"); box.setBoxIntegerField(10); pojo.box = box; String json = jsonb.toJson(pojo); assertEquals("{\"box\":{\"crateIntField\":11,\"crateStrField\":\"crateAdaptedBoxStr\"}}", json); AdaptedPojo result = jsonb.fromJson("{\"box\":{\"crateIntField\":10,\"crateStrField\":\"CrateStr\"}}", AdaptedPojo.class); assertEquals(Integer.valueOf(11), result.box.getBoxIntegerField()); assertEquals("boxAdaptedCrateStr", result.box.getBoxStrField()); } @Test public void testValueFieldAdapter() throws Exception { JsonbAdapter[] adapters = { new JsonbAdapter() { @Override public String adaptToJson(Integer integer) { return String.valueOf(integer); } @Override public Integer adaptFromJson(String s) { return Integer.valueOf(s); } } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo(); pojo.intField = 11; String json = jsonb.toJson(pojo); assertEquals("{\"intField\":\"11\"}", json); AdaptedPojo result = jsonb.fromJson("{\"intField\":\"10\"}", AdaptedPojo.class); assertEquals(Integer.valueOf(10), result.intField); } @Test public void testGenericAdapter() throws Exception { JsonbAdapter[] adapters = {new BoxToCrateCompatibleGenericsAdapter() { }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo<>(); pojo.strField = "POJO_STRING"; pojo.intBox = new GenericBox<>("INT_BOX_STR", 11); pojo.tBox = new GenericBox<>("T_BOX_STR", 110); String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); assertEquals("{\"intBox\":{\"adaptedT\":11,\"crateStrField\":\"INT_BOX_STR\"}," + "\"strField\":\"POJO_STRING\"," + "\"tBox\":{\"adaptedT\":110,\"crateStrField\":\"T_BOX_STR\"}}", marshalledJson); String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"Box3\",\"adaptedT\":33}," + "\"tBox\":{\"crateStrField\":\"tGenBoxCrateStr\",\"adaptedT\":22}," + "\"strField\":\"POJO_STRING\"," + "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); assertEquals("POJO_STRING", result.strField); assertEquals("Box3", result.intBox.getStrField()); assertEquals(33, result.intBox.getX()); assertEquals("tGenBoxCrateStr", result.tBox.getStrField()); assertEquals(22, result.tBox.getX()); assertEquals("strBoxStr", result.strBox.getStrField()); assertEquals("44", result.strBox.getX()); } @Test public void testPropagatedTypeArgs() throws Exception { JsonbAdapter[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo<>(); pojo.intBox = new GenericBox<>("INT_BOX_STR", 110); pojo.tBox = new GenericBox<>("T_BOX_STR", 111); pojo.strBox = new GenericBox<>("STR_BOX_STR", "101"); String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"INT_BOX_STR\"}," + "\"strBox\":{\"strField\":\"STR_BOX_STR\",\"x\":\"101\"}," + "\"tBox\":{\"adaptedT\":{\"x\":[\"111\"]},\"crateStrField\":\"T_BOX_STR\"}}", marshalledJson); String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"strCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"77\"]}}," + "\"tBox\":{\"crateStrField\":\"tStrCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"88\"]}}," + "\"strField\":\"POJO_STRING\"," + "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}"; AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); assertEquals("POJO_STRING", result.strField); assertEquals("strCrateStr", result.intBox.getStrField()); assertEquals(77, result.intBox.getX()); assertEquals("tStrCrateStr", result.tBox.getStrField()); assertEquals(88, result.tBox.getX()); assertEquals("strBoxStr", result.strBox.getStrField()); assertEquals("44", result.strBox.getX()); } @Test public void testStringToGenericCollectionAdapter() throws Exception { JsonbAdapter[] adapters = {new IntegerListToStringAdapter()}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo> pojo = new AdaptedPojo<>(); pojo.tVar = Arrays.asList(11, 22, 33); pojo.integerList = Arrays.asList(110, 111, 101); String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>>(){}.getType()); assertEquals("{\"integerList\":\"110#111#101\"," + "\"tVar\":\"11#22#33\"}", marshalledJson); String toUnmarshall = "{\"integerList\":\"11#22#33#44\",\"stringList\":[\"first\",\"second\"]," + "\"tVar\":\"110#111#101\"}"; AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>>(){}.getType()); List expectedIntegerList = Arrays.asList(11, 22, 33, 44); List expectedStringList = Arrays.asList("first", "second"); List expectedTList = Arrays.asList(110, 111, 101); assertEquals(expectedIntegerList, result.integerList); assertEquals(expectedStringList, result.stringList); assertEquals(expectedTList, result.tVar); } @Test public void testAdaptObjectInCollection() throws Exception { JsonbAdapter[] adapters = {new BoxToCrateCompatibleGenericsAdapter() { }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo<>(); pojo.tGenericBoxList = new ArrayList<>(); pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_1", 110)); pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_2", 101)); String marshalledJson = jsonb.toJson(pojo, new TestTypeToken>(){}.getType()); assertEquals("{\"tGenericBoxList\":[{\"adaptedT\":110,\"crateStrField\":\"GEN_BOX_STR_1\"},{\"adaptedT\":101,\"crateStrField\":\"GEN_BOX_STR_2\"}]}", marshalledJson); String toUnmarshall = "{\"integerList\":[11,22,33,44],\"stringList\":[\"first\",\"second\"]," + "\"tGenericBoxList\":[{\"crateStrField\":\"FirstCrate\",\"adaptedT\":11},{\"crateStrField\":\"SecondCrate\",\"adaptedT\":22}]}"; AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken>(){}.getType()); assertEquals("FirstCrate", result.tGenericBoxList.get(0).getStrField()); assertEquals("SecondCrate", result.tGenericBoxList.get(1).getStrField()); assertEquals(Integer.valueOf(11), result.tGenericBoxList.get(0).getX()); assertEquals(Integer.valueOf(22), result.tGenericBoxList.get(1).getX()); } @Test public void testAdaptTypeIntoCollection() throws Exception { JsonbAdapter[] adapters = {new JsonbAdapter>() { @Override public List adaptToJson(String s) { List result = new ArrayList<>(); for (String str : s.split(",")) { result.add(Integer.parseInt(str)); } return result; } @Override public String adaptFromJson(List ints) { StringBuilder sb = new StringBuilder(); for (Integer i : ints) { if (!sb.toString().isEmpty()) { sb.append(","); } sb.append(i); } return sb.toString(); } } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); String json = "{\"strValues\":[11,22,33]}"; final NonGenericPojo object = new NonGenericPojo(); object.strValues = "11,22,33"; assertEquals(json, jsonb.toJson(object)); NonGenericPojo pojo = jsonb.fromJson(json, NonGenericPojo.class); assertEquals("11,22,33", pojo.strValues); } @Test public void testMarshallGenericField() throws Exception { JsonbAdapter[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo adaptedPojo = new AdaptedPojo<>(); adaptedPojo.tBox = new GenericBox<>("tGenBoxStrField", 22); adaptedPojo.intBox = new GenericBox<>("genBoxStrField", 11); String json = jsonb.toJson(adaptedPojo, new TestTypeToken>(){}.getType()); assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"11\"]},\"crateStrField\":\"genBoxStrField\"},\"tBox\":{\"adaptedT\":{\"x\":[\"22\"]},\"crateStrField\":\"tGenBoxStrField\"}}", json); AdaptedPojo unmarshalledAdaptedPojo = jsonb.fromJson(json, new TestTypeToken>(){}.getType()); assertEquals("genBoxStrField", unmarshalledAdaptedPojo.intBox.getStrField()); assertEquals(Integer.valueOf(11), unmarshalledAdaptedPojo.intBox.getX()); } @Test public void testTypeVariable() throws Exception { JsonbAdapter[] adapters = {new JsonbAdapter>, BigDecimal>() { @Override public BigDecimal adaptToJson(List> genericBoxes) { return BigDecimal.valueOf(genericBoxes.get(0).getX()); } @Override public List> adaptFromJson(BigDecimal bigDecimal) { List> list = new ArrayList<>(); list.add(new GenericBox<>("", bigDecimal.doubleValue())); return list; } }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo>> intBoxPojo = new AdaptedPojo<>(); List> intBoxList = new ArrayList<>(); intBoxList.add(new GenericBox<>("", 11d)); intBoxPojo.tVar = intBoxList; String json = jsonb.toJson(intBoxPojo, new TestTypeToken>>>(){}.getType()); assertEquals("{\"tVar\":11.0}", json); AdaptedPojo>> result = jsonb.fromJson(json, new TestTypeToken>>>(){}.getType()); assertEquals(Double.valueOf(11), result.tVar.get(0).getX()); } @Test public void testAdaptRoot() throws Exception { JsonbAdapter[] adapters = {new JsonbAdapter() { @Override public Crate adaptToJson(Box box) { return new Crate(box.getBoxStrField(), box.getBoxIntegerField()); } @Override public Box adaptFromJson(Crate crate) { return new Box(crate.getCrateStrField(), crate.getCrateIntField()); } }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); Box pojo = new Box("BOX_STR", 101); String marshalledJson = jsonb.toJson(pojo); assertEquals("{\"crateIntField\":101,\"crateStrField\":\"BOX_STR\"}", marshalledJson); Box result = jsonb.fromJson("{\"crateIntField\":110,\"crateStrField\":\"CRATE_STR\"}", Box.class); assertEquals("CRATE_STR", result.getBoxStrField()); assertEquals(Integer.valueOf(110), result.getBoxIntegerField()); } @Test public void testAdaptMapString() throws Exception { JsonbAdapter[] adapters = {new JsonbAdapter, String>() { @Override public Map adaptFromJson(String obj) throws Exception { final HashMap result = new HashMap<>(); result.put("fake", 101); return result; } @Override public String adaptToJson(Map obj) throws Exception { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : obj.entrySet()) { if (sb.length() > 0) { sb.append("#"); } sb.append(entry.getKey()).append("-").append(entry.getValue()); } return sb.toString(); } }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo<>(); pojo.stringIntegerMap = new HashMap<>(); pojo.stringIntegerMap.put("first", 11); pojo.stringIntegerMap.put("second", 22); pojo.tMap = new HashMap<>(pojo.stringIntegerMap); String marshalledJson = jsonb.toJson(pojo, new AdaptedPojo(){}.getClass()); assertEquals("{\"stringIntegerMap\":\"first-11#second-22\",\"tMap\":\"first-11#second-22\"}", marshalledJson); AdaptedPojo result = jsonb.fromJson("{\"stringIntegerMap\":\"fake-value\",\"tMap\":\"fake-value\"}", new TestTypeToken>(){}.getType()); assertEquals(Integer.valueOf(101), result.stringIntegerMap.get("fake")); assertEquals(Integer.valueOf(101), result.tMap.get("fake")); } @Test public void testAdaptMapToObject() throws Exception { JsonbAdapter[] adapters = {new JsonbAdapter, Crate>() { @Override public Map adaptFromJson(Crate obj) throws Exception { final HashMap fake = new HashMap<>(); fake.put("fake", "11"); return fake; } @Override public Crate adaptToJson(Map obj) throws Exception { final Map.Entry next = obj.entrySet().iterator().next(); return new Crate(next.getKey(), Integer.parseInt(next.getValue())); } }}; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters)); AdaptedPojo pojo = new AdaptedPojo<>(); pojo.tMap = new HashMap<>(); pojo.tMap.put("first", "101"); TestTypeToken> typeToken = new TestTypeToken>() {}; String marshalledJson = jsonb.toJson(pojo, typeToken.getType()); assertEquals("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", marshalledJson); AdaptedPojo result = jsonb.fromJson("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", typeToken.getType()); assertEquals("11", result.tMap.get("fake")); } @Test public void testAdaptJsonObject() { JsonObjectPojo pojo = new JsonObjectPojo(); pojo.box = new Box("strFieldValue", 110); String json = defaultJsonb.toJson(pojo); assertEquals("{\"box\":{\"boxStrField\":\"strFieldValue\",\"boxIntegerField\":110}}", json); JsonObjectPojo result = defaultJsonb.fromJson(json, JsonObjectPojo.class); assertEquals("strFieldValue", result.box.getBoxStrField()); assertEquals(Integer.valueOf(110), result.box.getBoxIntegerField()); } @Test public void testAdaptAuthor() { Author author = new Author(); author.setFirstName("John"); author.setLastName("Connor"); String json = defaultJsonb.toJson(author); assertEquals("{\"firstName\":\"J\",\"lastName\":\"Connor\"}", json); Author result = defaultJsonb.fromJson("{\"firstName\":\"J\",\"lastName\":\"Connor\"}", Author.class); assertEquals("\"J\"", result.getFirstName()); assertEquals("Connor", result.getLastName()); } @Test public void testAdapterReturningNull() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withAdapters(new ReturnNullAdapter()).withNullValues(true)); ScalarValueWrapper wrapper = new ScalarValueWrapper<>(); wrapper.setValue(10); Type type = new TestTypeToken>() { }.getType(); String json = jsonb.toJson(wrapper, type); assertEquals("{\"value\":null}", json); ScalarValueWrapper result = jsonb.fromJson("{\"value\":null}", type); assertNull(result.getValue()); } @Test public void testAdaptUUID() { UUIDContainer pojo = new UUIDContainer(); UUID uuid = UUID.fromString("b329da91-0d96-44b6-b466-56c2458b2877"); pojo.setUuidClsBased(uuid); pojo.setUuidIfcBased(uuid); String result = defaultJsonb.toJson(pojo); assertEquals("{\"uuidClsBased\":\"b329da91-0d96-44b6-b466-56c2458b2877\",\"uuidIfcBased\":\"b329da91-0d96-44b6-b466-56c2458b2877\"}", result); UUIDContainer uuidContainer = defaultJsonb.fromJson(result, UUIDContainer.class); assertEquals(uuid, uuidContainer.getUuidClsBased()); assertEquals(uuid, uuidContainer.getUuidIfcBased()); } @Test public void testSupertypeAdapter() { SupertypeAdapterPojo pojo = new SupertypeAdapterPojo(); pojo.setNumberInteger(10); pojo.setSerializableInteger(11); assertEquals("{\"numberInteger\":\"11\",\"serializableInteger\":12}", defaultJsonb.toJson(pojo)); pojo = defaultJsonb.fromJson("{\"numberInteger\":\"11\",\"serializableInteger\":12}", SupertypeAdapterPojo.class); assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); assertEquals(Integer.valueOf(11), pojo.getSerializableInteger()); } public static class PropertyTypeMismatch { private Throwable error = new RuntimeException("foo"); public Optional getError() { return Optional.ofNullable(error); } public void setError(Instant errorTime) { this.error = new RuntimeException("Error at: " + errorTime.toString()); } } public static class ThrowableAdapter implements JsonbAdapter> { public int callCount = 0; @Override public Map adaptToJson(Throwable obj) throws Exception { HashMap output = new HashMap<>(); output.put("message", obj.getMessage()); output.put("type", obj.getClass().getName()); callCount++; return unmodifiableMap(output); } @Override public Throwable adaptFromJson(Map obj) throws Exception { throw new UnsupportedOperationException("not implemented"); } } /** * Serialize a class that has mismatching properties. The field is of type * Throwable but the getter method is of type Optional. The user-defined * adapter for Throwable should still be called. */ @Test public void testOptionalAdapter() { ThrowableAdapter adapter = new ThrowableAdapter(); Jsonb jsonb = JsonbBuilder.newBuilder().withConfig(new JsonbConfig().withAdapters(adapter)).build(); PropertyTypeMismatch obj = new PropertyTypeMismatch(); String json = jsonb.toJson(obj); assertEquals("{\"error\":{\"message\":\"foo\",\"type\":\"java.lang.RuntimeException\"}}", json); assertEquals(1, adapter.callCount, "The user-defined ThrowableAdapter should have been called"); } public static class InstantAdapter implements JsonbAdapter { public int callCount = 0; @Override public String adaptToJson(Instant obj) throws Exception { return obj.toString(); } @Override public Instant adaptFromJson(String obj) throws Exception { callCount++; if (obj.equals("CUSTOM_VALUE")) return Instant.MAX; return Instant.parse(obj); } } /** * Make sure that the same property can use a different adapter for * serialization and deserialization. */ @Test public void testDifferentAdapters() { ThrowableAdapter throwableAdapter = new ThrowableAdapter(); InstantAdapter instantAdapter = new InstantAdapter(); Jsonb jsonb = JsonbBuilder.newBuilder() .withConfig(new JsonbConfig().withAdapters(throwableAdapter, instantAdapter)) .build(); String json = "{\"error\":\"CUSTOM_VALUE\"}"; PropertyTypeMismatch obj = jsonb.fromJson(json, PropertyTypeMismatch.class); assertEquals("Error at: +1000000000-12-31T23:59:59.999999999Z", obj.getError().get().getMessage()); assertEquals(1, instantAdapter.callCount); String afterJson = jsonb.toJson(obj); assertEquals("{\"error\":{\"message\":\"Error at: +1000000000-12-31T23:59:59.999999999Z\",\"type\":\"java.lang.RuntimeException\"}}", afterJson); assertEquals(1, throwableAdapter.callCount); } public static class StringAdapter implements JsonbAdapter { @Override public String adaptToJson(String obj) throws Exception { return obj.toUpperCase(); } @Override public String adaptFromJson(String obj) throws Exception { return obj.toLowerCase(); } } /** * Test for: https://github.com/eclipse-ee4j/yasson/issues/346 */ @Test public void testAdaptedRootType() { Jsonb jsonb = JsonbBuilder.newBuilder() .withConfig(new JsonbConfig().withAdapters(new StringAdapter())) .build(); String original = "hello world!"; assertEquals("\"HELLO WORLD!\"", jsonb.toJson(original)); assertEquals(original, jsonb.fromJson("\"HELLO WORLD!\"", String.class)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.adapters.model.*; import jakarta.json.bind.JsonbException; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.bind.annotation.JsonbTypeAdapter; /** * @author Roman Grigoriadi */ public class JsonbTypeAdapterTest { public static class BoxToStringAdapter implements JsonbAdapter { @Override public Box adaptFromJson(String obj) throws Exception { String[] strings = obj.split(":"); return new Box(strings[0], Integer.valueOf(strings[1])); } @Override public String adaptToJson(Box obj) throws Exception { return obj.getBoxStrField()+":"+obj.getBoxIntegerField(); } } public static class IncompatibleAdapterPojo { @JsonbTypeAdapter(BoxToStringAdapter.class) public String str; } public static class AnnotatedPojo { @JsonbTypeAdapter(BoxToStringAdapter.class) public Box box; @JsonbTypeAdapter(BoxToCratePropagatedIntegerStringAdapter.class) public GenericBox tBox; @JsonbTypeAdapter(BoxToCratePropagatedIntegerStringAdapter.class) public GenericBox xBox; } @Test public void testIncompatibleAdapter() throws Exception { IncompatibleAdapterPojo incompatibleAdapterFieldPojo = new IncompatibleAdapterPojo(); incompatibleAdapterFieldPojo.str = "STR"; try { defaultJsonb.toJson(incompatibleAdapterFieldPojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("Adapter of runtime type class")); assertTrue(e.getMessage().contains("does not match property type ")); } } @Test public void testGenericFieldsMatch() { AnnotatedPojo annotatedPojo = new AnnotatedPojo<>(); annotatedPojo.tBox = new GenericBox<>("T_BOX", 110); String marshalledJson = defaultJsonb.toJson(annotatedPojo, new TestTypeToken>(){}.getType()); assertEquals("{\"tBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"T_BOX\"}}", marshalledJson); AnnotatedPojo result = defaultJsonb.fromJson("{\"tBox\":{\"crateStrField\":\"T_BOX\",\"adaptedT\":{\"x\":[\"110\"]}}}", new TestTypeToken>(){}.getType()); assertEquals("T_BOX", result.tBox.getStrField()); assertEquals(Integer.valueOf(110), result.tBox.getX()); } @Test public void testAnnotatedTbox() throws Exception { AnnotatedPojo pojo = new AnnotatedPojo(); pojo.box = new Box("STR", 101); String marshalledJson = defaultJsonb.toJson(pojo); assertEquals("{\"box\":\"STR:101\"}", marshalledJson); AnnotatedPojo result = defaultJsonb.fromJson("{\"box\":\"STR:110\"}", AnnotatedPojo.class); assertEquals("STR", result.box.getBoxStrField()); assertEquals(Integer.valueOf(110), result.box.getBoxIntegerField()); } @Test public void testBoxWithTypeAdapter() { BoxWithAdapter boxWithAdapter = new BoxWithAdapter("STR", 101); String marshalledJson = defaultJsonb.toJson(boxWithAdapter); assertEquals("{\"boxInteger\":101,\"boxStr\":\"STR\"}", marshalledJson); BoxWithAdapter result = defaultJsonb.fromJson("{\"boxInteger\":101,\"boxStr\":\"STR\"}", BoxWithAdapter.class); assertEquals("STR", result.getBoxStrField()); assertEquals(Integer.valueOf(101), result.getBoxIntegerField()); } @Test public void testBoxWithTypeSerializer() { BoxWithSerializer boxWithSerializer = new BoxWithSerializer("STR", 101); String marshalledJson = defaultJsonb.toJson(boxWithSerializer); assertEquals("{\"boxInteger\":101,\"boxStr\":\"STR\"}", marshalledJson); } @Test public void testBoxWithTypeDeserializer() { BoxWithDeserializer result = defaultJsonb.fromJson("{\"boxInteger\":101,\"boxStr\":\"STR\"}", BoxWithDeserializer.class); assertEquals("STR", result.getBoxStrField()); assertEquals(Integer.valueOf(101), result.getBoxIntegerField()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/AdaptedPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.List; import java.util.Map; /** * @author Roman Grigoriadi */ public class AdaptedPojo { public String strField; public Integer intField; public Box box; public GenericBox intBox; public GenericBox strBox; public GenericBox tBox; public List> tGenericBoxList; public List integerList; public List stringList; public Map stringIntegerMap; public Map tMap; public T tVar; } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/Author.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeAdapter; public class Author { @JsonbTypeAdapter(FirstNameAdapter.class) private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/Box.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; /** * @author Roman Grigoriadi */ public class Box { private String boxStrField; private Integer boxIntegerField; public Box() { } public Box(String boxStrField, Integer boxIntegerField) { this.boxStrField = boxStrField; this.boxIntegerField = boxIntegerField; } public String getBoxStrField() { return boxStrField; } public void setBoxStrField(String boxStrField) { this.boxStrField = boxStrField; } public Integer getBoxIntegerField() { return boxIntegerField; } public void setBoxIntegerField(Integer boxIntegerField) { this.boxIntegerField = boxIntegerField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxToCrateCompatibleGenericsAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; /** * @author Roman Grigoriadi */ public abstract class BoxToCrateCompatibleGenericsAdapter implements JsonbAdapter,GenericCrate> { @Override public GenericCrate adaptToJson(GenericBox tGenericBox) { GenericCrate genericCrate = new GenericCrate<>(); genericCrate.setCrateStrField(tGenericBox.getStrField()); genericCrate.setT(tGenericBox.getX()); return genericCrate; } @Override public GenericBox adaptFromJson(GenericCrate tGenericCrate) { GenericBox genericBox = new GenericBox<>(); genericBox.setStrField(tGenericCrate.getCrateStrField()); genericBox.setX(tGenericCrate.getT()); return genericBox; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedIntegerStringAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.ArrayList; import java.util.List; /** * @author Roman Grigoriadi */ public class BoxToCratePropagatedIntegerStringAdapter extends BoxToCratePropagatedTypeArgsAdapter { @Override public GenericCrate>> adaptToJson(GenericBox integerGenericBox) { if (integerGenericBox == null) { return null; } GenericCrate>> crate = new GenericCrate<>(); crate.setCrateStrField(integerGenericBox.getStrField()); crate.setT(new GenericBox<>()); crate.getT().setX(new ArrayList<>()); crate.getT().getX().add(integerGenericBox.getX().toString()); return crate; } @Override public GenericBox adaptFromJson(GenericCrate>> boxGenericCrate) { if (boxGenericCrate == null) { return null; } GenericBox genericBox = new GenericBox<>(); genericBox.setStrField(boxGenericCrate.getCrateStrField()); genericBox.setX(Integer.parseInt(boxGenericCrate.getT().getX().get(0))); return genericBox; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedTypeArgsAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; import java.util.List; /** * @author Roman Grigoriadi */ public abstract class BoxToCratePropagatedTypeArgsAdapter implements JsonbAdapter, GenericCrate>>> { } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxToJsonObjectAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.spi.JsonProvider; /** * @author Roman Grigoriadi */ public class BoxToJsonObjectAdapter implements JsonbAdapter { @Override public JsonObject adaptToJson(Box obj) throws Exception { final JsonObjectBuilder builder = JsonProvider.provider().createObjectBuilder(); builder.add("boxStrField", obj.getBoxStrField()); builder.add("boxIntegerField", obj.getBoxIntegerField()); return builder.build(); } @Override public Box adaptFromJson(JsonObject jsonObj) throws Exception { Box box = new Box(); box.setBoxStrField(jsonObj.getString("boxStrField")); box.setBoxIntegerField(jsonObj.getInt("boxIntegerField")); return box; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapter.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeAdapter; /** * @author David Kral */ @JsonbTypeAdapter(BoxWithAdapterAdapter.class) public class BoxWithAdapter { private String boxStrField; private Integer boxIntegerField; public BoxWithAdapter() { } public BoxWithAdapter(String boxStrField, Integer boxIntegerField) { this.boxStrField = boxStrField; this.boxIntegerField = boxIntegerField; } public String getBoxStrField() { return boxStrField; } public void setBoxStrField(String boxStrField) { this.boxStrField = boxStrField; } public Integer getBoxIntegerField() { return boxIntegerField; } public void setBoxIntegerField(Integer boxIntegerField) { this.boxIntegerField = boxIntegerField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapterAdapter.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.spi.JsonProvider; /** * @author David Kral */ public class BoxWithAdapterAdapter implements JsonbAdapter { @Override public JsonObject adaptToJson(BoxWithAdapter obj) { final JsonObjectBuilder builder = JsonProvider.provider().createObjectBuilder(); builder.add("boxInteger", obj.getBoxIntegerField()); builder.add("boxStr", obj.getBoxStrField()); return builder.build(); } @Override public BoxWithAdapter adaptFromJson(JsonObject jsonObj) { BoxWithAdapter box = new BoxWithAdapter(); box.setBoxIntegerField(jsonObj.getInt("boxInteger")); box.setBoxStrField(jsonObj.getString("boxStr")); return box; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; /** * @author David Kral */ @JsonbTypeDeserializer(BoxWithDeserializerDeserializer.class) public class BoxWithDeserializer { private String boxStrField; private Integer boxIntegerField; public BoxWithDeserializer() { } public BoxWithDeserializer(String boxStrField, Integer boxIntegerField) { this.boxStrField = boxStrField; this.boxIntegerField = boxIntegerField; } public String getBoxStrField() { return boxStrField; } public void setBoxStrField(String boxStrField) { this.boxStrField = boxStrField; } public Integer getBoxIntegerField() { return boxIntegerField; } public void setBoxIntegerField(Integer boxIntegerField) { this.boxIntegerField = boxIntegerField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializerDeserializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; /** * @author David Kral */ public class BoxWithDeserializerDeserializer implements JsonbDeserializer { @Override public BoxWithDeserializer deserialize(JsonParser jsonParser, DeserializationContext ctx, Type type) { BoxWithDeserializer box = new BoxWithDeserializer(); while (jsonParser.hasNext()) { JsonParser.Event next = jsonParser.next(); if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("boxInteger")) { box.setBoxIntegerField(ctx.deserialize(Integer.class, jsonParser)); } else if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("boxStr")) { box.setBoxStrField(ctx.deserialize(String.class, jsonParser)); } } return box; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeSerializer; /** * @author David Kral */ @JsonbTypeSerializer(BoxWithSerializerSerializer.class) public class BoxWithSerializer { private String boxStrField; private Integer boxIntegerField; public BoxWithSerializer() { } public BoxWithSerializer(String boxStrField, Integer boxIntegerField) { this.boxStrField = boxStrField; this.boxIntegerField = boxIntegerField; } public String getBoxStrField() { return boxStrField; } public void setBoxStrField(String boxStrField) { this.boxStrField = boxStrField; } public Integer getBoxIntegerField() { return boxIntegerField; } public void setBoxIntegerField(Integer boxIntegerField) { this.boxIntegerField = boxIntegerField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializerSerializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * @author David Kral */ public class BoxWithSerializerSerializer implements JsonbSerializer { @Override public void serialize(BoxWithSerializer boxWithSerializer, JsonGenerator jsonGenerator, SerializationContext serializationContext) { jsonGenerator .writeStartObject() .writeKey("boxInteger").write(boxWithSerializer.getBoxIntegerField()) .writeKey("boxStr").write(boxWithSerializer.getBoxStrField()) .writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/Chain.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; public class Chain { private String name; private Chain linksTo; private Foo has; public Chain(String name) { this.name = name; } public Chain() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Chain getLinksTo() { return linksTo; } public void setLinksTo(Chain linksTo) { this.linksTo = linksTo; } public Foo getHas() { return has; } public void setHas(Foo has) { this.has = has; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.LinkedHashMap; import java.util.Map; import jakarta.json.bind.adapter.JsonbAdapter; public class ChainAdapter implements JsonbAdapter>{ @Override public Map adaptToJson(Chain obj) throws Exception { Map map = new LinkedHashMap<>(); map.put("has", obj.getHas()); map.put("linksTo", obj.getLinksTo()); map.put("name", obj.getName()); return map; } @SuppressWarnings("unchecked") @Override public Chain adaptFromJson(Map obj) throws Exception { if(obj != null) { Chain chain = new Chain((String) obj.get("name")); chain.setHas((Foo) obj.get("has")); adaptFromJson((Map) obj.get("linksTo")); return chain; } else { return null; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class ChainSerializer implements JsonbSerializer{ public static final String RECURSIVE_REFERENCE_ERROR = "There is a recursive reference"; @Override public void serialize(Chain obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); if(obj.getHas() != null) { ctx.serialize("has", obj.getHas(), generator); } if(obj.getLinksTo() != null) { ctx.serialize("linksTo", obj.getLinksTo(), generator); } generator.write("name", obj.getName()); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/Crate.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; /** * @author Roman Grigoriadi */ public class Crate { private String crateStrField; private Integer crateIntField; public Crate() { } public Crate(String crateStrField, Integer crateIntField) { this.crateStrField = crateStrField; this.crateIntField = crateIntField; } public String getCrateStrField() { return crateStrField; } public void setCrateStrField(String crateStrField) { this.crateStrField = crateStrField; } public Integer getCrateIntField() { return crateIntField; } public void setCrateIntField(Integer crateIntField) { this.crateIntField = crateIntField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/FirstNameAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.Json; import jakarta.json.JsonValue; import jakarta.json.bind.adapter.JsonbAdapter; public class FirstNameAdapter implements JsonbAdapter { @Override public JsonValue adaptToJson(String firstName) { return Json.createValue(firstName.subSequence(0,1).toString()); } @Override public String adaptFromJson(JsonValue json) { return json.toString(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/Foo.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; public class Foo { private String bar; public Foo(String bar) { this.bar = bar; } public String getBar() { return bar; } public void setBar(String bar) { this.bar = bar; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.HashMap; import java.util.Map; import jakarta.json.bind.adapter.JsonbAdapter; public class FooAdapter implements JsonbAdapter>{ @Override public Map adaptToJson(Foo obj) throws Exception { Map map = new HashMap<>(); map.put("bar", obj.getBar()); return map; } @Override public Foo adaptFromJson( Map obj) throws Exception { return new Foo(obj.get("bar").toString()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class FooSerializer implements JsonbSerializer{ @Override public void serialize(Foo obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("bar", obj.getBar()); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/GenericBox.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; /** * @author Roman Grigoriadi */ public class GenericBox { private String strField; private X x; public GenericBox(String strField, X x) { this.strField = strField; this.x = x; } public GenericBox() { } public X getX() { return x; } public String getStrField() { return strField; } public void setStrField(String strField) { this.strField = strField; } public void setX(X x) { this.x = x; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/GenericCrate.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class GenericCrate { private String crateStrField; @JsonbProperty("adaptedT") private T t; public GenericCrate(String crateStrField, T t) { this.crateStrField = crateStrField; this.t = t; } public GenericCrate() { } public String getCrateStrField() { return crateStrField; } public T getT() { return t; } public void setCrateStrField(String crateStrField) { this.crateStrField = crateStrField; } public void setT(T t) { this.t = t; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/IntegerListToStringAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; import java.util.ArrayList; import java.util.List; /** * @author Roman Grigoriadi */ public class IntegerListToStringAdapter implements JsonbAdapter, String> { @Override public String adaptToJson(List integers) { StringBuilder sb = new StringBuilder(); for (Integer integer : integers) { if (!sb.toString().isEmpty()) { sb.append("#"); } sb.append(integer); } return sb.toString(); } @Override public List adaptFromJson(String s) { String[] items = s.split("#"); List integerList = new ArrayList<>(); for (String item : items) { integerList.add(Integer.parseInt(item)); } return integerList; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/JsonObjectPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeAdapter; /** * @author Roman Grigoriadi */ public class JsonObjectPojo { @JsonbTypeAdapter(BoxToJsonObjectAdapter.class) public Box box; public GenericBox genericBox; } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; import java.lang.reflect.Constructor; import java.util.stream.Stream; /** * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime. */ public abstract class LocalPolymorphicAdapter implements JsonbAdapter> { private final String[] allowedClasses; /** * Create new instance. * * @param allowedClasses allowed classes for loading by name */ public LocalPolymorphicAdapter(final Class... allowedClasses) { this.allowedClasses = Stream.of(allowedClasses).map(Class::getName).toArray(value -> new String[allowedClasses.length]); } /** * Returns all classes which are allowed for loading. * * @return allowed classes for loading by name */ public String[] getAllowedClasses() { return allowedClasses; } @Override public LocalTypeWrapper adaptToJson(T obj) throws Exception { LocalTypeWrapper wrapper = new LocalTypeWrapper<>(); wrapper.setClassName(obj.getClass().getName()); wrapper.setInstance(obj); return wrapper; } @Override @SuppressWarnings("unchecked") public final T adaptFromJson(LocalTypeWrapper obj) throws Exception { if (!isAllowed(obj.getClassName())) { throw new ClassNotFoundException(obj.getClassName()); } Constructor constructor = (Constructor) Class.forName(obj.getClassName()).getConstructor(); T instance = constructor.newInstance(); populateInstance(instance, obj); return instance; } protected abstract void populateInstance(T instance, LocalTypeWrapper obj); private boolean isAllowed(String name) { for (String className : allowedClasses) { if (className.equals(name)) { return true; } } return false; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/LocalTypeWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; /** * Created by Roman Grigoriadi (roman.grigoriadi@oracle.com) on 08/06/2017. */ public class LocalTypeWrapper { private String className; private E instance; /** * Gets class name. * * @return Class name. */ public String getClassName() { return className; } /** * Sets class name. * * @param className Class name to set. */ public void setClassName(String className) { this.className = className; } /** * Gets instance. * * @return Instance. */ public E getInstance() { return instance; } /** * Sets instance. * * @param instance Instance to set. */ public void setInstance(E instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/MultiinterfaceAdapter.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; import java.io.Serializable; interface MultiinterfaceAdapter extends Serializable, JsonbAdapter { } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; public abstract class MultilevelAdapterClass implements MultiinterfaceAdapter { } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; public class NumberAdapter implements JsonbAdapter { @Override public String adaptToJson(Number obj) throws Exception { return Integer.valueOf(((Integer)obj) + 1).toString(); } @Override public Number adaptFromJson(String obj) throws Exception { return Integer.parseInt(obj) - 1; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/ReturnNullAdapter.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; public class ReturnNullAdapter implements JsonbAdapter { @Override public String adaptToJson(Number obj) throws Exception { return null; } @Override public Number adaptFromJson(String obj) throws Exception { return null; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/SerializableAdapter.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.adapter.JsonbAdapter; import java.io.Serializable; public class SerializableAdapter implements JsonbAdapter { @Override public Integer adaptToJson(Serializable obj) throws Exception { return Integer.valueOf(obj.toString()) + 1; } @Override public Serializable adaptFromJson(Integer obj) throws Exception { return obj - 1; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/SupertypeAdapterPojo.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeAdapter; public class SupertypeAdapterPojo { @JsonbTypeAdapter(NumberAdapter.class) private Integer numberInteger; @JsonbTypeAdapter(SerializableAdapter.class) private Integer serializableInteger; public Integer getNumberInteger() { return numberInteger; } public void setNumberInteger(Integer numberInteger) { this.numberInteger = numberInteger; } public Integer getSerializableInteger() { return serializableInteger; } public void setSerializableInteger(Integer serializableInteger) { this.serializableInteger = serializableInteger; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/UUIDContainer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import jakarta.json.bind.annotation.JsonbTypeAdapter; import java.util.UUID; public class UUIDContainer { @JsonbTypeAdapter(UUIDMapperClsBased.class) private UUID uuidClsBased; @JsonbTypeAdapter(UUIDMapperIfcBased.class) private UUID uuidIfcBased; public UUID getUuidClsBased() { return uuidClsBased; } public void setUuidClsBased(UUID uuidClsBased) { this.uuidClsBased = uuidClsBased; } public UUID getUuidIfcBased() { return uuidIfcBased; } public void setUuidIfcBased(UUID uuidIfcBased) { this.uuidIfcBased = uuidIfcBased; } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.Optional; import java.util.UUID; public class UUIDMapperClsBased extends MultilevelAdapterClass { @Override public String adaptToJson(UUID obj) throws Exception { return Optional.ofNullable(obj).map(UUID::toString).orElse(null); } @Override public UUID adaptFromJson(String obj) throws Exception { return Optional.ofNullable(obj).map(UUID::fromString).orElse(null); } } ================================================ FILE: src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.adapters.model; import java.util.Optional; import java.util.UUID; public class UUIDMapperIfcBased implements MultiinterfaceAdapter { @Override public String adaptToJson(UUID obj) throws Exception { return Optional.ofNullable(obj).map(UUID::toString).orElse(null); } @Override public UUID adaptFromJson(String obj) throws Exception { return Optional.ofNullable(obj).map(UUID::fromString).orElse(null); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/AnnotationInheritanceTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.InheritedAnnotationsPojo; /** * @author Roman Grigoriadi */ public class AnnotationInheritanceTest { @Test public void testAnnotationInheritance() { InheritedAnnotationsPojo pojo = new InheritedAnnotationsPojo(); String expectedJson = "{}"; assertEquals(expectedJson, defaultJsonb.toJson(pojo)); InheritedAnnotationsPojo result = defaultJsonb.fromJson("{\"renamedProperty\":\"abc\"}", InheritedAnnotationsPojo.class); assertEquals("abc", result.property); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/EncodingTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.TestTypeToken; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; import java.util.logging.Logger; /** * Tests encoding to JSONP propagation * * @author Roman Grigoriadi */ public class EncodingTest { private static final Logger logger = Logger.getLogger(EncodingTest.class.getName()); private static final String[] CZECH; private static final String[] RUSSIAN; static { ResourceBundle messages = ResourceBundle.getBundle("yasson-messages"); CZECH = messages.getString("czechDiacritics").split(","); RUSSIAN = messages.getString("russianDiacritics").split(","); } @Test public void testCP1250Encoding() throws UnsupportedEncodingException { String encoding = "cp1250"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); testMarshaller(CZECH, jsonb, encoding); testUnmarshaller(CZECH, jsonb, encoding); } @Test public void testUTF8Encoding() throws UnsupportedEncodingException { String encoding = "UTF-8"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); testMarshaller(CZECH, jsonb, encoding); testUnmarshaller(CZECH, jsonb, encoding); testMarshaller(RUSSIAN, jsonb, encoding); testUnmarshaller(RUSSIAN, jsonb, encoding); } @Test public void testcp1251Encoding() throws UnsupportedEncodingException { String encoding = "cp1251"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding)); testMarshaller(RUSSIAN, jsonb, encoding); testUnmarshaller(RUSSIAN, jsonb, encoding); } private static void testMarshaller(String[] input, Jsonb jsonb, String encoding) throws UnsupportedEncodingException { List strings = Arrays.asList(input); ByteArrayOutputStream baos = new ByteArrayOutputStream(); jsonb.toJson(strings, baos); String marshallerResult = baos.toString(encoding); logger.finest("Marshaller JSON result: "+marshallerResult); assertEquals(diacriticsToJsonArray(input), marshallerResult); } private static void testUnmarshaller(String[] input, Jsonb jsonb, String encoding) throws UnsupportedEncodingException { String json = diacriticsToJsonArray(input); logger.finest("JSON for unmarshaller: "+json); InputStream bis = new ByteArrayInputStream(json.getBytes(encoding)); ArrayList result = jsonb.fromJson(bis, new TestTypeToken>(){}.getType()); assertArrayEquals(input, result.toArray(new String[result.size()])); } private static String diacriticsToJsonArray(String[] diacritics) { StringBuilder sb = new StringBuilder(); sb.append("["); for (String str : diacritics) { sb.append("\"").append(str).append("\","); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.Animal; import org.eclipse.yasson.customization.model.Dog; import org.eclipse.yasson.customization.model.ImplementationClassPojo; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.util.HashMap; import static org.eclipse.yasson.YassonConfig.USER_TYPE_MAPPING; public class ImplementationClassTest { @Test public void testAnnotatedImplementation() { ImplementationClassPojo pojo = new ImplementationClassPojo(); Animal dog = new Dog("Bulldog"); pojo.setAnimal(dog); String expected = "{\"animal\":{\"dogProperty\":\"Bulldog\"}}"; String json = defaultJsonb.toJson(pojo); assertEquals(expected, json); ImplementationClassPojo result = defaultJsonb.fromJson(expected, ImplementationClassPojo.class); assertTrue(result.getAnimal() instanceof Dog); assertEquals("Bulldog", ((Dog)result.getAnimal()).getDogProperty()); } @Test public void testJsonbConfigUserImplementation() { HashMap userMapping = new HashMap<>(); userMapping.put(Animal.class, Dog.class); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(USER_TYPE_MAPPING, userMapping)); Animal animal = new Dog("Bulldog"); String expected = "{\"dogProperty\":\"Bulldog\"}"; String json = jsonb.toJson(animal); assertEquals(expected, json); Dog result = (Dog) jsonb.fromJson("{\"dogProperty\":\"Bulldog\"}", Animal.class); assertEquals("Bulldog", result.getDogProperty()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/InterfaceAnnotationsTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.InterfacedPojoB; import org.eclipse.yasson.customization.model.InterfacedPojoImpl; /** * @author Roman Grigoriadi */ public class InterfaceAnnotationsTest { @Test public void testJsonbPropertyIfcInheritance() { InterfacedPojoB pojo = new InterfacedPojoImpl(); pojo.setPropertyA("AA"); pojo.setPropertyB("BB"); final String json = "{\"propA\":\"AA\",\"propB\":\"BB\"}"; assertEquals(json, defaultJsonb.toJson(pojo)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import java.math.BigDecimal; import java.time.LocalDate; import java.util.Set; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbNumberFormat; import jakarta.json.bind.annotation.JsonbProperty; import org.eclipse.yasson.customization.model.CreatorConstructorPojo; import org.eclipse.yasson.customization.model.CreatorFactoryMethodPojo; import org.eclipse.yasson.customization.model.CreatorIncompatibleTypePojo; import org.eclipse.yasson.customization.model.CreatorMultipleDeclarationErrorPojo; import org.eclipse.yasson.customization.model.CreatorPackagePrivateConstructor; import org.eclipse.yasson.customization.model.CreatorWithoutJavabeanProperty; import org.eclipse.yasson.customization.model.CreatorWithoutJsonbProperty1; import org.eclipse.yasson.customization.model.ParameterNameTester; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * @author Roman Grigoriadi */ public class JsonbCreatorTest { @Test public void testRootConstructor() { String json = "{\"str1\":\"abc\",\"str2\":\"def\",\"bigDec\":25}"; CreatorConstructorPojo pojo = defaultJsonb.fromJson(json, CreatorConstructorPojo.class); assertEquals("abc", pojo.str1); assertEquals("def", pojo.str2); assertEquals(new BigDecimal("25"), pojo.bigDec); } @Test public void testRootFactoryMethod() { String json = "{\"par1\":\"abc\",\"par2\":\"def\",\"bigDec\":25}"; CreatorFactoryMethodPojo pojo = defaultJsonb.fromJson(json, CreatorFactoryMethodPojo.class); assertEquals("abc", pojo.str1); assertEquals("def", pojo.str2); assertEquals(new BigDecimal("25"), pojo.bigDec); } @Test public void testRootCreatorWithInnerCreator() { String json = "{\"str1\":\"abc\",\"str2\":\"def\",\"bigDec\":25, \"innerFactoryCreator\":{\"par1\":\"inn1\",\"par2\":\"inn2\",\"bigDec\":11}}"; CreatorConstructorPojo pojo = defaultJsonb.fromJson(json, CreatorConstructorPojo.class); assertEquals("abc", pojo.str1); assertEquals("def", pojo.str2); assertEquals(new BigDecimal("25"), pojo.bigDec); assertEquals("inn1", pojo.innerFactoryCreator.str1); assertEquals("inn2", pojo.innerFactoryCreator.str2); assertEquals(new BigDecimal("11"), pojo.innerFactoryCreator.bigDec); } @Test public void testIncompatibleFactoryMethodReturnType() { try { defaultJsonb.fromJson("{\"s1\":\"abc\"}", CreatorIncompatibleTypePojo.class); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("Return type of creator")); } } @Test public void testMultipleCreatorsError() { try { defaultJsonb.fromJson("{\"s1\":\"abc\"}", CreatorMultipleDeclarationErrorPojo.class); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("More than one @JsonbCreator")); } } @Test public void testCreatorWithoutJsonbParameters1() { //arg2 is missing in json document CreatorWithoutJsonbProperty1 object = defaultJsonb.fromJson("{\"arg0\":\"abc\", \"s2\":\"def\"}", CreatorWithoutJsonbProperty1.class); assertThat(object.getPar1(), is("abc")); assertThat(object.getPar2(), is("def")); assertThat(object.getPar3(), is((byte) 0)); } @Test public void testCreatorWithoutJavabeanProperty() { final CreatorWithoutJavabeanProperty result = defaultJsonb.fromJson("{\"s1\":\"abc\", \"s2\":\"def\"}", CreatorWithoutJavabeanProperty.class); assertEquals("abcdef", result.getStrField()); } @Test public void testPackagePrivateCreator() { assertThrows(JsonbException.class, () -> defaultJsonb.fromJson("{\"strVal\":\"abc\", \"intVal\":5}", CreatorPackagePrivateConstructor.class)); } @Test public void testLocalizedConstructor() { String json = "{\"localDate\":\"05-09-2017\"}"; DateConstructor result = defaultJsonb.fromJson(json, DateConstructor.class); assertEquals(LocalDate.of(2017, 9, 5), result.localDate); } @Test public void testLocalizedConstructorMergedWithProperty() { String json = "{\"localDate\":\"05-09-2017\"}"; DateConstructorMergedWithProperty result = defaultJsonb.fromJson(json, DateConstructorMergedWithProperty.class); assertEquals(LocalDate.of(2017, 9, 5), result.localDate); } @Test public void testLocalizedFactoryParameter() { String json = "{\"number\":\"10.000\"}"; FactoryNumberParam result = defaultJsonb.fromJson(json, FactoryNumberParam.class); assertEquals(BigDecimal.TEN, result.number); } @Test public void testLocalizedFactoryParameterMergedWithProperty() { String json = "{\"number\":\"10.000\"}"; FactoryNumberParamMergedWithProperty result = defaultJsonb.fromJson(json, FactoryNumberParamMergedWithProperty.class); assertEquals(BigDecimal.TEN, result.number); } @Test public void testCorrectCreatorParameterNames() { String json = "{\"string\":\"someText\", \"someParam\":null }"; ParameterNameTester result = defaultJsonb.fromJson(json, ParameterNameTester.class); assertEquals("someText", result.name); assertNull(result.secondParam); } @Test public void testGenericCreatorParameter() throws Exception { final String json = "{\"persons\": [{\"name\": \"name1\"}]}"; Persons persons = defaultJsonb.fromJson(json, Persons.class); assertEquals(1, persons.hiddenPersons.size()); assertEquals("name1", persons.hiddenPersons.iterator().next().getName()); } public static final class Persons { Set hiddenPersons; private Persons(Set persons) { this.hiddenPersons = persons; } @JsonbCreator public static Persons wrap(@JsonbProperty("persons") Set persons) { return new Persons(persons); } public Set getPersons() { return null; } } public static final class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public static final class DateConstructor { public LocalDate localDate; @JsonbCreator public DateConstructor(@JsonbProperty("localDate") @JsonbDateFormat(value = "dd-MM-yyyy", locale = "nl-NL") LocalDate localDate) { this.localDate = localDate; } } public static final class DateConstructorMergedWithProperty { @JsonbDateFormat(value = "dd-MM-yyyy", locale = "cs-CZ") public LocalDate localDate; @JsonbCreator public DateConstructorMergedWithProperty(@JsonbProperty("localDate") LocalDate localDate) { this.localDate = localDate; } } public static final class FactoryNumberParam { public BigDecimal number; private FactoryNumberParam(BigDecimal number) { this.number = number; } @JsonbCreator public static FactoryNumberParam createInstance( @JsonbProperty("number") @JsonbNumberFormat(value = "000.000", locale = "en-us") BigDecimal number) { return new FactoryNumberParam(number); } } public static final class FactoryNumberParamMergedWithProperty { @JsonbNumberFormat(value = "000.000", locale = "en-us") public BigDecimal number; private FactoryNumberParamMergedWithProperty(BigDecimal number) { this.number = number; } @JsonbCreator public static FactoryNumberParamMergedWithProperty createInstance(@JsonbProperty("number") BigDecimal number) { return new FactoryNumberParamMergedWithProperty(number); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/JsonbDateFormatterTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.DateFormatPojo; import org.eclipse.yasson.customization.model.DateFormatPojoWithClassLevelFormatter; import org.eclipse.yasson.customization.model.TrimmedDatePojo; import org.eclipse.yasson.internal.JsonBindingBuilder; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import static org.eclipse.yasson.YassonConfig.ZERO_TIME_PARSE_DEFAULTING; /** * Tests customization of date fields via {@link jakarta.json.bind.annotation.JsonbDateFormat} annotation * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbDateFormatterTest { @Test public void testCustomDateFormatSerialization() { final Calendar timeCalendar = new Calendar.Builder() .setDate(2017, Calendar.MARCH, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("UTC")) .build(); DateFormatPojo dateFormatPojo = new DateFormatPojo(); dateFormatPojo.plainDateField = timeCalendar.getTime(); dateFormatPojo.formattedDateField = timeCalendar.getTime(); dateFormatPojo.getterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.setterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndFieldFormattedDateField = timeCalendar.getTime(); dateFormatPojo.setterAndFieldFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndSetterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndSetterAndFieldFormattedDateField = timeCalendar.getTime(); String expectedJson = "{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"plainDateField\":\"2017-03-03T11:11:10Z[UTC]\",\"setterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"setterFormattedDateField\":\"2017-03-03T11:11:10Z[UTC]\"}"; assertEquals(expectedJson, defaultJsonb.toJson(dateFormatPojo)); } @Test public void testCustomDateFormatDeserialization() { final Calendar timeCalendar = new Calendar.Builder() .setDate(2017, Calendar.MARCH, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("UTC")) .build(); DateFormatPojo result = defaultJsonb.fromJson("{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 $$ 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"2017-03-03T11:11:10\",\"plainDateField\":\"2017-03-03T11:11:10\",\"setterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\"}", DateFormatPojo.class); assertEquals(timeCalendar.getTime(), result.plainDateField); assertEquals(timeCalendar.getTime(), result.formattedDateField); assertEquals(timeCalendar.getTime(), result.getterFormattedDateField); assertEquals(timeCalendar.getTime(), result.setterFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndFieldFormattedDateField); assertEquals(timeCalendar.getTime(), result.setterAndFieldFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndSetterFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndSetterAndFieldFormattedDateField); } @Test public void testCustomDateFormatSerializationWithClassLevelDateFormatterDefined() { final Calendar timeCalendar = new Calendar.Builder() .setDate(2017, Calendar.MARCH, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("UTC")) .build(); DateFormatPojoWithClassLevelFormatter dateFormatPojo = new DateFormatPojoWithClassLevelFormatter(); dateFormatPojo.plainDateField = timeCalendar.getTime(); dateFormatPojo.formattedDateField = timeCalendar.getTime(); dateFormatPojo.getterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.setterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndFieldFormattedDateField = timeCalendar.getTime(); dateFormatPojo.setterAndFieldFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndSetterFormattedDateField = timeCalendar.getTime(); dateFormatPojo.getterAndSetterAndFieldFormattedDateField = timeCalendar.getTime(); String expectedJson = "{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"plainDateField\":\"11:11:10 ^ 03-03-2017\",\"setterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^ 03-03-2017\"}"; assertEquals(expectedJson, defaultJsonb.toJson(dateFormatPojo)); } @Test public void testCustomDateFormatDeserializationWithClassLevelDateFormatterDefined() { final Calendar timeCalendar = new Calendar.Builder() .setDate(2017, Calendar.MARCH, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("UTC")) .build(); DateFormatPojoWithClassLevelFormatter result = defaultJsonb.fromJson("{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 $$ 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^ 03-03-2017\",\"plainDateField\":\"11:11:10 ^ 03-03-2017\",\"setterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\"}", DateFormatPojoWithClassLevelFormatter.class); assertEquals(timeCalendar.getTime(), result.plainDateField); assertEquals(timeCalendar.getTime(), result.formattedDateField); assertEquals(timeCalendar.getTime(), result.getterFormattedDateField); assertEquals(timeCalendar.getTime(), result.setterFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndFieldFormattedDateField); assertEquals(timeCalendar.getTime(), result.setterAndFieldFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndSetterFormattedDateField); assertEquals(timeCalendar.getTime(), result.getterAndSetterAndFieldFormattedDateField); } @Test public void testTrimmedDateParsing() { ZoneId utcZone = ZoneId.of("UTC"); ZonedDateTime zdt = ZonedDateTime.of(2018, 1, 30, 0, 0, 0, 0, utcZone); TrimmedDatePojo pojo = new TrimmedDatePojo(); pojo.setDate(Date.from(zdt.toInstant())); Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(utcZone)); calendar.setTime(pojo.getDate()); pojo.setCalendar(calendar); pojo.setLocalDateTime(zdt.toLocalDateTime()); pojo.setZonedDateTime(zdt); pojo.setZonedDateTimeNanosOfDay(zdt.plusNanos(3000)); pojo.setZonedDateTimeHoursAndSeconds(zdt); pojo.setZonedDateTimeOverriddenZone(zdt.withZoneSameInstant(ZoneId.of("Europe/Paris"))); pojo.setZonedInstant(zdt.withZoneSameInstant(ZoneId.of("Europe/Paris")).toInstant()); Jsonb zeroDefaultingJsonb = new JsonBindingBuilder() .withConfig(new JsonbConfig().setProperty(ZERO_TIME_PARSE_DEFAULTING, true)) .build(); String serialized = zeroDefaultingJsonb.toJson(pojo); assertEquals( "{\"calendar\":\"2018.01.30\",\"date\":\"2018.01.30\",\"localDateTime\":\"2018.01.30\",\"zonedDateTime\":\"2018.01.30\",\"zonedDateTimeHoursAndSeconds\":\"2018.01.30 00:00\",\"zonedDateTimeNanosOfDay\":\"2018.01.30 3000\",\"zonedDateTimeOverriddenZone\":\"2018.01.30 Europe/Paris\",\"zonedInstant\":\"2018.01.30 UTC\"}", serialized); String jsonToDeserialize = "{\"calendar\":\"2018.01.30\",\"date\":\"2018.01.30\",\"localDateTime\":\"2018.01.30\",\"zonedDateTime\":\"2018.01.30\",\"zonedDateTimeHoursAndSeconds\":\"2018.01.30 12:15\",\"zonedDateTimeNanosOfDay\":\"2018.01.30 9000\",\"zonedDateTimeOverriddenZone\":\"2018.01.30 Europe/Prague\",\"zonedInstant\":\"2018.01.30 Europe/Prague\"}"; TrimmedDatePojo trimmedDatePojo = zeroDefaultingJsonb.fromJson(jsonToDeserialize, TrimmedDatePojo.class); //Nanos are overridden in json for deserialization. Tests that defaulting hour/minute/second does not affect other units. assertEquals(2018, trimmedDatePojo.getZonedDateTimeNanosOfDay().getYear()); assertEquals(1, trimmedDatePojo.getZonedDateTimeNanosOfDay().getMonthValue()); assertEquals(30, trimmedDatePojo.getZonedDateTimeNanosOfDay().getDayOfMonth()); assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getHour()); assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getMinute()); assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getSecond()); assertEquals(9000, trimmedDatePojo.getZonedDateTimeNanosOfDay().getNano()); assertEquals(utcZone, trimmedDatePojo.getZonedDateTimeNanosOfDay().getZone()); //Test trimmed zoned date time have correct values. assertEquals(2018, trimmedDatePojo.getZonedDateTime().getYear()); assertEquals(1, trimmedDatePojo.getZonedDateTime().getMonthValue()); assertEquals(30, trimmedDatePojo.getZonedDateTime().getDayOfMonth()); assertEquals(0, trimmedDatePojo.getZonedDateTime().getHour()); assertEquals(0, trimmedDatePojo.getZonedDateTime().getMinute()); assertEquals(0, trimmedDatePojo.getZonedDateTime().getSecond()); assertEquals(utcZone, trimmedDatePojo.getZonedDateTime().getZone()); //Zone is overridden in JSON, causing assertEquals(2018, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getYear()); assertEquals(1, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getMonthValue()); assertEquals(30, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getDayOfMonth()); assertEquals(12, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getHour()); assertEquals(0, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getMinute()); assertEquals(15, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getSecond()); assertEquals(utcZone, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getZone()); //Defaulting UTC zone is overridden in JSON. assertEquals(2018, trimmedDatePojo.getZonedDateTimeOverriddenZone().getYear()); assertEquals(1, trimmedDatePojo.getZonedDateTimeOverriddenZone().getMonthValue()); assertEquals(30, trimmedDatePojo.getZonedDateTimeOverriddenZone().getDayOfMonth()); assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getHour()); assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getMinute()); assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getSecond()); assertEquals(ZoneId.of("Europe/Prague"), trimmedDatePojo.getZonedDateTimeOverriddenZone().getZone()); //Tests LocalDateTime trimmed values assertEquals(2018, trimmedDatePojo.getLocalDateTime().getYear()); assertEquals(1, trimmedDatePojo.getLocalDateTime().getMonthValue()); assertEquals(30, trimmedDatePojo.getLocalDateTime().getDayOfMonth()); assertEquals(0, trimmedDatePojo.getLocalDateTime().getHour()); assertEquals(0, trimmedDatePojo.getLocalDateTime().getMinute()); assertEquals(0, trimmedDatePojo.getLocalDateTime().getSecond()); //Test date and instant have correct time. assertEquals(zdt.toInstant().toEpochMilli(), trimmedDatePojo.getDate().getTime()); assertEquals(zdt.withZoneSameLocal(ZoneId.of("Europe/Prague")).toInstant().toEpochMilli(), trimmedDatePojo.getZonedInstant().toEpochMilli()); //Test calendar instance //Tests LocalDateTime trimmed values assertEquals(2018, trimmedDatePojo.getCalendar().get(Calendar.YEAR)); assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.MONTH)); assertEquals(30, trimmedDatePojo.getCalendar().get(Calendar.DAY_OF_MONTH)); assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.HOUR_OF_DAY)); assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.MINUTE)); assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.SECOND)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.annotation.JsonbProperty; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.JsonbNillableClassSecondLevel; import org.eclipse.yasson.customization.model.JsonbNillableOverriddenWithJsonbProperty; import org.eclipse.yasson.customization.model.JsonbNillableOverridesClass; import org.eclipse.yasson.customization.model.JsonbNillableOverridesInterface; import org.eclipse.yasson.customization.model.JsonbNillableValue; import org.eclipse.yasson.customization.model.packagelevelannotations.JsonbNillablePackageLevel; import org.eclipse.yasson.customization.model.packagelevelannotations.PackageLevelOverriddenWithClassLevel; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; /** * Tests a {@link jakarta.json.bind.annotation.JsonbNillable} annotation. * @author Roman Grigoriadi */ public class JsonbNillableTest { @Test public void testJsonbNillable() { JsonbNillableValue pojo = new JsonbNillableValue(); assertEquals("{\"nillableField\":null}", defaultJsonb.toJson(pojo)); } @Test public void testJsonbNillableOverriddenWithJsonbProperty() { JsonbNillableOverriddenWithJsonbProperty pojo = new JsonbNillableOverriddenWithJsonbProperty(); assertEquals("{}", defaultJsonb.toJson(pojo)); } @Test public void testPackageLevelNillable() { JsonbNillablePackageLevel pojo = new JsonbNillablePackageLevel(); assertEquals("{\"packageLevelNillableField\":null}", defaultJsonb.toJson(pojo)); } @Test public void testPackageLevelOverriddenWithClassLevel() { PackageLevelOverriddenWithClassLevel pojo = new PackageLevelOverriddenWithClassLevel(); assertEquals("{}", defaultJsonb.toJson(pojo)); } /** * Tests inheritance of annotations from interfaces. */ @Test public void testNillableInheritFromInterface() throws Exception { JsonbNillableClassSecondLevel pojo = new JsonbNillableClassSecondLevel(); assertEquals("{\"classNillable\":null}", defaultJsonb.toJson(pojo)); } @Test public void testInheritanceOverride() throws Exception { JsonbNillableOverridesInterface overridesInterface = new JsonbNillableOverridesInterface(); assertEquals("{}", defaultJsonb.toJson(overridesInterface)); JsonbNillableOverridesClass overridesClass = new JsonbNillableOverridesClass(); assertEquals("{}", defaultJsonb.toJson(overridesClass)); } @Test public void testNillableInConfig() { String jsonString = nullableJsonb.toJson(new ScalarValueWrapper(){}); assertEquals("{\"value\":null}", jsonString); } public static class PrimitiveNullBoolean { @JsonbProperty(nillable = true) private Boolean someBoolean; void setSomeBoolean(boolean value) { // note that value is a primitive boolean // leaving this empty, exception will be thrown before Yasson gets here. } } /** * Test for issue https://github.com/eclipse-ee4j/yasson/issues/399 */ @Test public void testNillableSomeBoolean() { Jsonb jsonb = JsonbBuilder.create(); String input = "{\"someBoolean\": null}"; PrimitiveNullBoolean deserialized = jsonb.fromJson(input, PrimitiveNullBoolean.class); assertNull(deserialized.someBoolean); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.JsonbPropertyName; import org.eclipse.yasson.customization.model.JsonbPropertyNameCollision; import org.eclipse.yasson.customization.model.JsonbPropertyNillable; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.config.PropertyNamingStrategy; /** * Tests parsing of {@link jakarta.json.bind.annotation.JsonbProperty} test. * @author Roman Grigoriadi */ public class JsonbPropertyTest { @Test public void testPropertyName() throws Exception { JsonbPropertyName pojo = new JsonbPropertyName(); pojo.setFieldAnnotatedName("FIELD_ANNOTATED"); pojo.setMethodAnnotName("METHOD_ANNOTATED"); pojo.setFieldOverriddenWithMethodAnnot("OVERRIDDEN_GETTER"); assertEquals("{\"fieldAnnotatedNameCustomized\":\"FIELD_ANNOTATED\",\"getterAnnotatedName\":\"METHOD_ANNOTATED\",\"getterOverriddenName\":\"OVERRIDDEN_GETTER\"}", defaultJsonb.toJson(pojo)); String toUnmarshall = "{\"fieldAnnotatedNameCustomized\":\"FIELD_ANNOTATED\",\"setterOverriddenName\":\"OVERRIDDEN_GETTER\",\"setterAnnotatedName\":\"METHOD_ANNOTATED\"}"; JsonbPropertyName result = defaultJsonb.fromJson(toUnmarshall, JsonbPropertyName.class); assertEquals("FIELD_ANNOTATED", result.getFieldAnnotatedName()); assertEquals("METHOD_ANNOTATED", result.getMethodAnnotName()); assertEquals("OVERRIDDEN_GETTER", result.getFieldOverriddenWithMethodAnnot()); } @Test public void testNameCollision() { JsonbPropertyNameCollision nameCollisionPojo = new JsonbPropertyNameCollision(); tryClash(() -> defaultJsonb.toJson(nameCollisionPojo)); tryClash(() -> defaultJsonb.fromJson("{}", JsonbPropertyNameCollision.class)); } private static void tryClash(Runnable clashCommand) { try { clashCommand.run(); fail(); } catch (Exception e) { assertTrue(e.getMessage().startsWith("Property pojoName clashes with property pojoNameCollision")); } } @Test public void testPropertyNillable() { JsonbPropertyNillable pojo = new JsonbPropertyNillable(); assertEquals("{\"nullField\":null}", defaultJsonb.toJson(pojo)); } @Test public void testRenamedGetterAndSetter() { // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/355 final RenamedGetterAndSetter b = new RenamedGetterAndSetter(); b.setTest("hi"); final String h = defaultJsonb.toJson(b); final String expectedJson = "{\"apple\":\"hi\"}"; assertEquals(expectedJson, h); //this passes final RenamedGetterAndSetter b1 = defaultJsonb.fromJson(h, RenamedGetterAndSetter.class); assertEquals("hi", b1.getTest()); //this fails but passes in 1.0.4 } @Test public void testRenamedGetterAndSetter2() { // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81 final Jsonb jsonb = JsonbBuilder.create( new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); bean1.setAPIDocumentation("REST"); final String json = jsonb.toJson(bean1); final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); } @Test public void testRenamedGetterAndSetter3() { // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81 final Jsonb jsonb = JsonbBuilder.create(); final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2(); bean1.setAPIDocumentation("REST"); final String json = jsonb.toJson(bean1); final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class); assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation()); } public static class RenamedGetterAndSetter { private String apple; @JsonbProperty("apple") public String getTest() { return apple; } @JsonbProperty("apple") public void setTest(String test) { this.apple = test; } } public static class RenamedGetterAndSetter2 { private String api; @JsonbProperty("api") public String getAPIDocumentation() { return api; } @JsonbProperty("api") public void setAPIDocumentation(String api) { this.api = api; } } /** * In this test getter / setter doesn't match to field "doi", because declared by javabean convention. * When model is parsed there are to properties: * Property for private field "doi", without customization. This property is not readable because field * is private without getter / setter. * And property "DOI" - getter / setter without a field with customization on getter renaming it to doi * in serialized document. * * Because first of those properties is not readable this should not raise naming clash error. */ @Test public void testNonConflictingProperties() { NonConflictingProperties nonConflictingProperties = new NonConflictingProperties(); nonConflictingProperties.setDOI("DOI value"); String json = defaultJsonb.toJson(nonConflictingProperties); assertEquals("{\"doi\":\"DOI value\"}", json); NonConflictingProperties result = defaultJsonb.fromJson("{\"DOI\":\"DOI value\"}", NonConflictingProperties.class); assertEquals("DOI value", result.getDOI()); } /** * Same problem as above but now field is public, so clash takes place. */ @Test public void testConflictingProperties() { ConflictingProperties conflictingProperties = new ConflictingProperties(); conflictingProperties.setDOI("DOI value"); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); try { jsonb.toJson(conflictingProperties); fail(); } catch (JsonbException e) { if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingProperties.")) { throw e; } } } /** * Tests clash with property altered by naming strategy. */ @Test public void testConflictingWithUpperCamelStrategy() { ConflictingWithUpperCamelStrategy pojo = new ConflictingWithUpperCamelStrategy(); pojo.setDOI("DOI value"); String json = defaultJsonb.toJson(pojo); assertEquals("{\"Doi\":\"DOI value\",\"doi\":\"DOI value\"}", json); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); try { jsonb.toJson(pojo); fail(); } catch (JsonbException e) { if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingWithUpperCamelStrategy.")) { throw e; } } } @Test public void testConflictingWithLowercaseStrategy() { // scenario raised by user here: https://github.com/eclipse-ee4j/yasson/issues/296 Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES)); assertEquals("{\"url\":\"http://foo.com\"}", jsonb.toJson(new ConflictingIfLowercase())); } public static class ConflictingIfLowercase { private String url = "foo.com"; public String getURL() { return "http://" + url; } } public static class NonConflictingProperties { private String doi; @JsonbProperty("doi") public String getDOI() { return doi; } public void setDOI(String doi) { this.doi = doi; } } public static class ConflictingProperties { public String doi; @JsonbProperty("doi") public String getDOI() { return doi; } public void setDOI(String doi) { this.doi = doi; } } public static class ConflictingWithUpperCamelStrategy { public String doi; @JsonbProperty("Doi") public String getDOI() { return doi; } public void setDOI(String doi) { this.doi = doi; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbVisibility; import jakarta.json.bind.config.PropertyVisibilityStrategy; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Tests custom {@link PropertyVisibilityStrategy} * * @author Roman Grigoriadi */ public class JsonbPropertyVisibilityStrategyTest { public static class FieldPojo { private String afield; private String bfield; public String cfield; public String dfield; public FieldPojo(String afield, String bfield, String cfield, String dfield) { this.afield = afield; this.bfield = bfield; this.cfield = cfield; this.dfield = dfield; } } public static class GetterPojo { public String getAgetter() { return "avalue"; } public String getBgetter() { return "bvalue"; } private String getCgetter() { return "cvalue"; } private String getDgetter() { return "dvalue"; } } @JsonbVisibility(TestVisibilityStrategy.class) public static final class AnnotatedPojo { private String afield; private String bfield; public String cfield; public String dfield; public AnnotatedPojo(String afield, String bfield, String cfield, String dfield) { this.afield = afield; this.bfield = bfield; this.cfield = cfield; this.dfield = dfield; } public String getAgetter() { return "avalue"; } public String getBgetter() { return "bvalue"; } private String getCgetter() { return "cvalue"; } private String getDgetter() { return "dvalue"; } } public static final class TestVisibilityStrategy implements PropertyVisibilityStrategy { @Override public boolean isVisible(Field field) { final String fieldName = field.getName(); return fieldName.equals("bfield") || fieldName.equals("cfield"); } @Override public boolean isVisible(Method method) { final String methodName = method.getName(); return methodName.equals("getBgetter") || methodName.equals("getCgetter"); } } /** * Tests applying for both public and nonpublic fields. */ @Test public void testFieldVisibilityStrategy() { JsonbConfig customizedConfig = new JsonbConfig(); customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { final String fieldName = field.getName(); return fieldName.equals("afield") || fieldName.equals("dfield"); } @Override public boolean isVisible(Method method) { throw new IllegalStateException("Not supported"); } }); FieldPojo fieldPojo = new FieldPojo("avalue", "bvalue", "cvalue", "dvalue"); Jsonb jsonb = JsonbBuilder.create(customizedConfig); assertEquals("{\"afield\":\"avalue\",\"dfield\":\"dvalue\"}", jsonb.toJson(fieldPojo)); } /** * Tests applying for both public and nonpublic getters. */ @Test public void testMethodVisibilityStrategy() { JsonbConfig customizedConfig = new JsonbConfig(); customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { throw new IllegalStateException("Not supported"); } @Override public boolean isVisible(Method method) { final String methodName = method.getName(); return methodName.equals("getAgetter") || methodName.equals("getDgetter"); } }); GetterPojo getterPojo = new GetterPojo(); Jsonb jsonb = JsonbBuilder.create(customizedConfig); assertEquals("{\"agetter\":\"avalue\",\"dgetter\":\"dvalue\"}", jsonb.toJson(getterPojo)); } @Test public void testAnnotatedPojo() { AnnotatedPojo fieldPojo = new AnnotatedPojo("avalue", "bvalue", "cvalue", "dvalue"); assertEquals("{\"bfield\":\"bvalue\",\"bgetter\":\"bvalue\",\"cfield\":\"cvalue\",\"cgetter\":\"cvalue\"}", defaultJsonb.toJson(fieldPojo)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/NumberFormatTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.customization.model.NumberFormatPojo; import org.eclipse.yasson.customization.model.NumberFormatPojoWithoutClassLevelFormatter; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Locale; import jakarta.json.bind.JsonbConfig; /** * Tests number format. * @author Roman Grigoriadi */ public class NumberFormatTest { private static final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withLocale(Locale.US)); @Test public void testSerialize() { NumberFormatPojo pojo = new NumberFormatPojo(); pojo.bigDecimal = BigDecimal.TEN; pojo.bigInteger = BigInteger.ONE; pojo.aDouble = .1d; pojo.aFloat = .35f; pojo.aLong = Long.MAX_VALUE; pojo.integer = Integer.MAX_VALUE; pojo.aShort = 1; pojo.aByte = 127; pojo.setDoubleGetterFormatted(.1d); pojo.setDoubleSetterFormatted(.5d); pojo.setDoubleSetterAndPropertyFormatter(0.6d); String expectedJson = "{\"aByte\":\"127\",\"aDouble\":\"000.10000000\",\"aFloat\":\"000.34999999\",\"aLong\":\"9223372036854775807\",\"aShort\":\"00001\",\"bigDecimal\":\"00000010.000000\",\"bigInteger\":\"00000001\",\"doubleGetterFormatted\":\"000.10000000\",\"doubleSetterAndPropertyFormatter\":\"000.600\",\"doubleSetterFormatted\":\"0.5\",\"integer\":\"2147483647.0\"}"; assertEquals(expectedJson, jsonb.toJson(pojo)); } @Test public void testDeserializer() { String expectedJson = "{\"aByte\":\"127\",\"aDouble\":\"000.10000000\",\"aFloat\":\"000.34999999\",\"aLong\":\"9223372036854775807\",\"aShort\":\"00001\",\"bigDecimal\":\"00000010.000000\",\"bigInteger\":\"00000001\",\"doubleGetterFormatted\":\"000.10000\",\"doubleSetterFormatted\":\",005\",\"doubleSetterAndPropertyFormatter\":\"000,600\",\"integer\":\"2147483647.0\"}"; NumberFormatPojo pojo = jsonb.fromJson(expectedJson, NumberFormatPojo.class); assertEquals(BigDecimal.TEN, pojo.bigDecimal); assertEquals(BigInteger.ONE, pojo.bigInteger); assertEquals(Byte.valueOf((byte) 127), pojo.aByte); assertEquals(Double.valueOf(.1d), pojo.aDouble); assertEquals(Float.valueOf(.35f), pojo.aFloat); assertEquals((Integer)Integer.MAX_VALUE, pojo.integer); assertEquals(Short.valueOf((short) 1), pojo.aShort); assertEquals((Long)Long.MAX_VALUE, pojo.aLong); assertEquals(Double.valueOf(.1d), pojo.getDoubleGetterFormatted()); assertEquals(Double.valueOf(.005d), pojo.getDoubleSetterFormatted()); assertEquals(Double.valueOf(.6d), pojo.getDoubleSetterAndPropertyFormatter()); } @Test public void testSerializeWithoutClassLevelFormatter() { NumberFormatPojoWithoutClassLevelFormatter pojo = new NumberFormatPojoWithoutClassLevelFormatter(); pojo.setDoubleGetterFormatted(.1d); pojo.setDoubleSetterFormatted(.5d); pojo.setDoubleSetterAndPropertyFormatter(0.6d); String expectedJson = "{\"doubleGetterFormatted\":\"000.10000000\",\"doubleSetterAndPropertyFormatter\":\"000.600\",\"doubleSetterFormatted\":0.5}"; assertEquals(expectedJson, jsonb.toJson(pojo)); } @Test public void testDeserializeWithoutClassLevelFormatter() { String expectedJson = "{\"doubleGetterFormatted\":\"000.10000\",\"doubleSetterFormatted\":\",005\",\"doubleSetterAndPropertyFormatter\":\"000,600\"}"; NumberFormatPojoWithoutClassLevelFormatter pojo = jsonb.fromJson(expectedJson, NumberFormatPojoWithoutClassLevelFormatter.class); assertEquals(Double.valueOf(.1d), pojo.getDoubleGetterFormatted()); assertEquals(Double.valueOf(.005d), pojo.getDoubleSetterFormatted()); assertEquals(Double.valueOf(.6d), pojo.getDoubleSetterAndPropertyFormatter()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/PrettyPrintTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import java.util.Arrays; /** * Tests pretty print to JSONP propagation * * @author Roman Grigoriadi */ public class PrettyPrintTest { @Test public void testPrettyPrint() { assertEquals("[\n \"first\",\n \"second\"\n]", formattingJsonb.toJson(Arrays.asList("first", "second"))); } @Test public void testPrettyPrintFalse() { assertEquals("[\"first\",\"second\"]", defaultJsonb.toJson(Arrays.asList("first", "second"))); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.model.FieldCustomOrder; import org.eclipse.yasson.customization.model.FieldCustomOrderWrapper; import org.eclipse.yasson.customization.model.FieldOrder; import org.eclipse.yasson.customization.model.FieldOrderNameAnnotation; import org.eclipse.yasson.customization.model.FieldSpecificOrder; import org.eclipse.yasson.customization.model.RenamedPropertiesContainer; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbPropertyOrder; import jakarta.json.bind.config.PropertyOrderStrategy; /** * @author Roman Grigoriadi */ public class PropertyOrderTest { @Test public void testPropertySorting() { FieldOrder fieldOrder = new FieldOrder(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); String expectedLexicographical = "{\"aField\":\"aValue\",\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\"}"; assertEquals(expectedLexicographical, jsonb.toJson(fieldOrder)); jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); String expectedReverse = "{\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\",\"aField\":\"aValue\"}"; assertEquals(expectedReverse, jsonb.toJson(fieldOrder)); } @Test public void testPropertyCustomOrder() { FieldCustomOrder fieldCustomOrder = new FieldCustomOrder(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); String expectedCustomOrder = "{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"}"; assertEquals(expectedCustomOrder, jsonb.toJson(fieldCustomOrder)); FieldCustomOrderWrapper fieldCustomOrderWrapper = new FieldCustomOrderWrapper(); String expectedOrder = "{\"fieldCustomOrder\":{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"},\"intField\":1,\"stringField\":\"stringValue\"}"; assertEquals(expectedOrder, jsonb.toJson(fieldCustomOrderWrapper)); } @Test public void testPropertySetCustomOrder() { FieldSpecificOrder fieldSpecificOrder = new FieldSpecificOrder(); String expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"bField\":\"bValue\",\"cField\":\"cValue\"}"; assertEquals(expectedSpecific, defaultJsonb.toJson(fieldSpecificOrder)); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; assertEquals(expectedSpecific, jsonb.toJson(fieldSpecificOrder)); } @Test public void testPropertySortingWithNamingAnnotation() { FieldOrderNameAnnotation fieldOrderNameAnnotation = new FieldOrderNameAnnotation(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); String expectedLexicographical = "{\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"zField\":\"aValue\"}"; assertEquals(expectedLexicographical, jsonb.toJson(fieldOrderNameAnnotation)); jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); String expectedReverse = "{\"zField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}"; assertEquals(expectedReverse, jsonb.toJson(fieldOrderNameAnnotation)); } @Test public void testLexicographicalPropertyOrderRenamedProperties() { JsonbConfig config = new JsonbConfig(); config.setProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, PropertyOrderStrategy.LEXICOGRAPHICAL); Jsonb jsonb = JsonbBuilder.create(config); String jsonString = jsonb.toJson(new RenamedPropertiesContainer() {{ setStringInstance("Test String"); setLongInstance(1); }}); assertTrue(jsonString.matches("\\{\\s*\"first\"\\s*\\:\\s*0\\s*,\\s*\"second\"\\s*\\:\\s*\"Test String\"\\s*,\\s*\"third\"\\s*\\:\\s*1\\s*\\}")); RenamedPropertiesContainer unmarshalledObject = jsonb.fromJson("{ \"first\" : 1, \"second\" : \"Test String\", \"third\" : 1 }", RenamedPropertiesContainer.class); assertEquals(3, unmarshalledObject.getIntInstance()); } @Test public void testJsonbPropertyOrderOnRenamedProperties() { assertEquals("{\"c\":11,\"d\":10,\"aExtra\":\"extra\"}", defaultJsonb.toJson(new Range(10, 11))); } // By default, this object would use A-X ordering with property order: // anExtraProp, propA, propB // But with JsonbPropertyOrder we will put props 'propB' and 'propA' first, and leftovers will go at the end, resulting in: // propB, propA, anExtraProp @JsonbPropertyOrder({"propB","propA"}) public class Range { @JsonbProperty("d") public final int propA; @JsonbProperty("c") public final int propB; @JsonbProperty("aExtra") public final String anExtraProp = "extra"; @JsonbCreator public Range( @JsonbProperty("d") int propA, @JsonbProperty("c") int propB ) { this.propA = propA; this. propB = propB; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization; import java.util.Optional; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import org.eclipse.yasson.YassonConfig; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests for Yasson specific config properties. */ public class YassonSpecificConfigTests { private static final String NULL_VALUE_STRING = "null value handled"; private static final String NULL_VALUE_SERIALIZED = "\"" + NULL_VALUE_STRING + "\""; @Test public void nullRootSerializerTest() { Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(null)); } @Test public void emptyOptionalRootSerializerTest() { Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(Optional.empty())); } @Test public void nullSerializerNotUsedTest() { Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer())); assertEquals("[null]", jsonb.toJson(new String[] {null})); } private static final class RootNullSerializer implements JsonbSerializer { @Override public void serialize(Object obj, JsonGenerator generator, SerializationContext ctx) { generator.write(NULL_VALUE_STRING); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/Animal.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; public interface Animal { } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CollectionsWithFormatters.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import java.util.List; import jakarta.json.bind.annotation.JsonbNumberFormat; @JsonbNumberFormat(value = "000.000", locale = "en-us") public class CollectionsWithFormatters { public List doubleList; @JsonbNumberFormat(locale = "da-da") public List doubleList2; public List doubleList3; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorConstructorPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import java.math.BigDecimal; /** * @author Roman Grigoriadi */ public class CreatorConstructorPojo { public String str1; public String str2; public BigDecimal bigDec; public CreatorFactoryMethodPojo innerFactoryCreator; @JsonbCreator public CreatorConstructorPojo(@JsonbProperty("str1") String str1, @JsonbProperty("str2") String str2) { this.str1 = str1; this.str2 = str2; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorFactoryMethodPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import java.math.BigDecimal; /** * @author Roman Grigoriadi */ public class CreatorFactoryMethodPojo { public final String str1; public final String str2; public BigDecimal bigDec; private CreatorFactoryMethodPojo(String str1, String str2) { this.str1 = str1; this.str2 = str2; } @JsonbCreator public static CreatorFactoryMethodPojo getInstance(@JsonbProperty("par1") String str1, @JsonbProperty("par2")String str2) { return new CreatorFactoryMethodPojo(str1, str2); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorIncompatibleTypePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import java.io.Serializable; /** * @author Roman Grigoriadi */ public class CreatorIncompatibleTypePojo { public String s1; @JsonbCreator public static Serializable getInstance() { return null; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorMultipleDeclarationErrorPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class CreatorMultipleDeclarationErrorPojo { public String s1; @JsonbCreator public CreatorMultipleDeclarationErrorPojo(@JsonbProperty("val1") String s1) { this.s1 = s1; } @JsonbCreator public static CreatorMultipleDeclarationErrorPojo getInstance() { return null; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorPackagePrivateConstructor.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class CreatorPackagePrivateConstructor { private String strVal; private final int intVal; @JsonbCreator CreatorPackagePrivateConstructor(@JsonbProperty("intVal") int intVal) { this.intVal = intVal; } public String getStrVal() { return strVal; } public void setStrVal(String strVal) { this.strVal = strVal; } public int getIntVal() { return intVal; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJavabeanProperty.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class CreatorWithoutJavabeanProperty { private String strField; @JsonbCreator public CreatorWithoutJavabeanProperty(@JsonbProperty("s1") String s1, @JsonbProperty("s2") String s2) { this.strField = s1 + s2; } public String getStrField() { return strField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class CreatorWithoutJsonbProperty { private final String par1; private final String par2; private double par3; @JsonbCreator public CreatorWithoutJsonbProperty(@JsonbProperty("s1") String par1, String par2, double d1) { this.par1 = par1; this.par2 = par2; this.par3 = d1; } public String getPar1() { return par1; } public String getPar2() { return par2; } public double getPar3() { return par3; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty1.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class CreatorWithoutJsonbProperty1 { private final String par1; private final String par2; private byte par3; @JsonbCreator public CreatorWithoutJsonbProperty1(String par1, @JsonbProperty("s2") String par2, byte par3) { this.par1 = par1; this.par2 = par2; this.par3 = par3; } public String getPar1() { return par1; } public String getPar2() { return par2; } public byte getPar3() { return par3; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/DateFormatPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.util.Date; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class DateFormatPojo { public Date plainDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date formattedDateField; public Date getterFormattedDateField; public Date setterFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getterAndFieldFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date setterAndFieldFormattedDateField; public Date getterAndSetterFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getterAndSetterAndFieldFormattedDateField; public Date getPlainDateField() { return plainDateField; } public void setPlainDateField(Date plainDateField) { this.plainDateField = plainDateField; } public Date getFormattedDateField() { return formattedDateField; } public void setFormattedDateField(Date formattedDateField) { this.formattedDateField = formattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterFormattedDateField() { return getterFormattedDateField; } public void setGetterFormattedDateField(Date getterFormattedDateField) { this.getterFormattedDateField = getterFormattedDateField; } public Date getSetterFormattedDateField() { return setterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public void setSetterFormattedDateField(Date setterFormattedDateField) { this.setterFormattedDateField = setterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndFieldFormattedDateField() { return getterAndFieldFormattedDateField; } public void setGetterAndFieldFormattedDateField(Date getterAndFieldFormattedDateField) { this.getterAndFieldFormattedDateField = getterAndFieldFormattedDateField; } public Date getSetterAndFieldFormattedDateField() { return setterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public void setSetterAndFieldFormattedDateField(Date setterAndFieldFormattedDateField) { this.setterAndFieldFormattedDateField = setterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndSetterFormattedDateField() { return getterAndSetterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public void setGetterAndSetterFormattedDateField(Date getterAndSetterFormattedDateField) { this.getterAndSetterFormattedDateField = getterAndSetterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndSetterAndFieldFormattedDateField() { return getterAndSetterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss $$ dd-MM-yyyy", locale = "Europe/Copenhagen") public void setGetterAndSetterAndFieldFormattedDateField(Date getterAndSetterAndFieldFormattedDateField) { this.getterAndSetterAndFieldFormattedDateField = getterAndSetterAndFieldFormattedDateField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/DateFormatPojoWithClassLevelFormatter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.util.Date; /** * The pojo class that used to test {@link JsonbDateFormat} when the containing class has a default {@link JsonbDateFormat} defined * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ @JsonbDateFormat(value = "HH:mm:ss ^ dd-MM-yyyy", locale = "Europe/Copenhagen") public class DateFormatPojoWithClassLevelFormatter { public Date plainDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date formattedDateField; public Date getterFormattedDateField; public Date setterFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getterAndFieldFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date setterAndFieldFormattedDateField; public Date getterAndSetterFormattedDateField; @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getterAndSetterAndFieldFormattedDateField; public Date getPlainDateField() { return plainDateField; } public void setPlainDateField(Date plainDateField) { this.plainDateField = plainDateField; } public Date getFormattedDateField() { return formattedDateField; } public void setFormattedDateField(Date formattedDateField) { this.formattedDateField = formattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterFormattedDateField() { return getterFormattedDateField; } public void setGetterFormattedDateField(Date getterFormattedDateField) { this.getterFormattedDateField = getterFormattedDateField; } public Date getSetterFormattedDateField() { return setterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public void setSetterFormattedDateField(Date setterFormattedDateField) { this.setterFormattedDateField = setterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndFieldFormattedDateField() { return getterAndFieldFormattedDateField; } public void setGetterAndFieldFormattedDateField(Date getterAndFieldFormattedDateField) { this.getterAndFieldFormattedDateField = getterAndFieldFormattedDateField; } public Date getSetterAndFieldFormattedDateField() { return setterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public void setSetterAndFieldFormattedDateField(Date setterAndFieldFormattedDateField) { this.setterAndFieldFormattedDateField = setterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndSetterFormattedDateField() { return getterAndSetterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public void setGetterAndSetterFormattedDateField(Date getterAndSetterFormattedDateField) { this.getterAndSetterFormattedDateField = getterAndSetterFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen") public Date getGetterAndSetterAndFieldFormattedDateField() { return getterAndSetterAndFieldFormattedDateField; } @JsonbDateFormat(value = "HH:mm:ss $$ dd-MM-yyyy", locale = "Europe/Copenhagen") public void setGetterAndSetterAndFieldFormattedDateField(Date getterAndSetterAndFieldFormattedDateField) { this.getterAndSetterAndFieldFormattedDateField = getterAndSetterAndFieldFormattedDateField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/Dog.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; public class Dog implements Animal { private String dogProperty; public Dog() { } public Dog(String dogProperty) { this.dogProperty = dogProperty; } public String getDogProperty() { return dogProperty; } public void setDogProperty(String dogProperty) { this.dogProperty = dogProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrder.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbPropertyOrder; /** * @author David Kral */ @JsonbPropertyOrder({"aField", "cField", "dField" ,"bField"}) public class FieldCustomOrder { public String aField = "aValue"; public String dField = "dValue"; public String bField = "bValue"; public String cField = "cValue"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrderWrapper.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author David Kral */ public class FieldCustomOrderWrapper { public String stringField = "stringValue"; public int intField = 1; public FieldCustomOrder fieldCustomOrder = new FieldCustomOrder(); } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/FieldOrder.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author David Kral */ public class FieldOrder { public String aField = "aValue"; public String dField = "dValue"; public String bField = "bValue"; public String cField = "cValue"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/FieldOrderNameAnnotation.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author David Kral */ public class FieldOrderNameAnnotation { @JsonbProperty(value = "zField") public String aField = "aValue"; public String dField = "dValue"; public String bField = "bValue"; public String cField = "cValue"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/FieldSpecificOrder.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbPropertyOrder; /** * @author David Kral */ @JsonbPropertyOrder({"aField", "dField"}) public class FieldSpecificOrder { public String aField = "aValue"; public String dField = "dValue"; public String bField = "bValue"; public String cField = "cValue"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/ImplementationClassPojo.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import org.eclipse.yasson.ImplementationClass; public class ImplementationClassPojo { @ImplementationClass(Dog.class) private Animal animal; public Animal getAnimal() { return animal; } public void setAnimal(Animal animal) { this.animal = animal; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InheritedAnnotationsPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author Roman Grigoriadi */ @InheritsNillableRecursion public class InheritedAnnotationsPojo { @InheritsJsonbProperty public String property; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InheritsJsonbProperty.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @JsonbProperty("renamedProperty") @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public @interface InheritsJsonbProperty { String value() default "abc"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InheritsNillable.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; @JsonbNillable() @Retention(RetentionPolicy.RUNTIME) @Target({ANNOTATION_TYPE, TYPE, PACKAGE}) public @interface InheritsNillable { String value() default "abc"; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InheritsNillableRecursion.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; @InheritsNillable @Retention(RetentionPolicy.RUNTIME) @Target({ANNOTATION_TYPE, TYPE, PACKAGE}) public @interface InheritsNillableRecursion { } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoA.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public interface InterfacedPojoA { @JsonbProperty("propA") String getPropertyA(); @JsonbProperty("propA") void setPropertyA(String property); } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoAbsImpl.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author Roman Grigoriadi */ public abstract class InterfacedPojoAbsImpl implements InterfacedPojoA { private String propertyA; @Override public String getPropertyA() { return propertyA; } @Override public void setPropertyA(String property) { this.propertyA = property; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoB.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public interface InterfacedPojoB extends InterfacedPojoA { @JsonbProperty("propB") String getPropertyB(); @JsonbProperty("propB") void setPropertyB(String propertyB); } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoC.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public interface InterfacedPojoC extends InterfacedPojoB { @JsonbProperty("propC") String getPropertyC(); @JsonbProperty("propC") void setPropertyC(String propertyC); } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoImpl.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author Roman Grigoriadi */ public class InterfacedPojoImpl extends InterfacedPojoAbsImpl implements InterfacedPojoB { private String propertyB; @Override public String getPropertyB() { return propertyB; } @Override public void setPropertyB(String propertyB) { this.propertyB = propertyB; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassFirstLevel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * Implements a class with nillable annotation. * * @author Roman Grigoriadi */ public class JsonbNillableClassFirstLevel implements JsonbNillableInterfaceBaseOne { private String classNillable; public String getClassNillable() { return classNillable; } public void setClassNillable(String classNillable) { this.classNillable = classNillable; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassSecondLevel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * Extends a class which implements interface which is nillable. * * @author Roman Grigoriadi */ public class JsonbNillableClassSecondLevel extends JsonbNillableClassFirstLevel { private String subclassNillable; public String getSubclassNillable() { return subclassNillable; } public void setSubclassNillable(String subclassNillable) { this.subclassNillable = subclassNillable; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBase.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; /** * All extending classes should be nillable. * @author Roman Grigoriadi */ @JsonbNillable public interface JsonbNillableInterfaceBase { } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseOne.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; /** * @author Roman Grigoriadi */ public interface JsonbNillableInterfaceBaseOne extends JsonbNillableInterfaceBase { } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseTwo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; /** * Overrides super interface nillable value. * * @author Roman Grigoriadi */ @JsonbNillable(false) public interface JsonbNillableInterfaceBaseTwo extends JsonbNillableInterfaceBase { } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverriddenWithJsonbProperty.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class JsonbNillableOverriddenWithJsonbProperty { @JsonbProperty("nillableOverriddenAccidentallyWithDefaultValue") private String nillableOverriddenWithFieldJsonbProperty; private String nillableOverriddenWithGetterJsonbProperty; private String nillableOverriddenWithSetterJsonbProperty; public String getNillableOverriddenWithFieldJsonbProperty() { return nillableOverriddenWithFieldJsonbProperty; } public void setNillableOverriddenWithFieldJsonbProperty(String nillableOverriddenWithFieldJsonbProperty) { this.nillableOverriddenWithFieldJsonbProperty = nillableOverriddenWithFieldJsonbProperty; } @JsonbProperty(nillable = false) public String getNillableOverriddenWithGetterJsonbProperty() { return nillableOverriddenWithGetterJsonbProperty; } public void setNillableOverriddenWithGetterJsonbProperty(String nillableOverriddenWithGetterJsonbProperty) { this.nillableOverriddenWithGetterJsonbProperty = nillableOverriddenWithGetterJsonbProperty; } public String getNillableOverriddenWithSetterJsonbProperty() { return nillableOverriddenWithSetterJsonbProperty; } @JsonbProperty(nillable = false) public void setNillableOverriddenWithSetterJsonbProperty(String nillableOverriddenWithSetterJsonbProperty) { this.nillableOverriddenWithSetterJsonbProperty = nillableOverriddenWithSetterJsonbProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesClass.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ @JsonbNillable public class JsonbNillableOverridesClass extends JsonbNillableOverridesInterface { @JsonbProperty("overridesNillableInParent") public String nillableField; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesInterface.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; /** * @author Roman Grigoriadi */ @JsonbNillable(false) public class JsonbNillableOverridesInterface implements JsonbNillableInterfaceBaseTwo { public String nillableField; } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbNillableValue.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNillable; /** * @author Roman Grigoriadi */ @JsonbNillable public class JsonbNillableValue { private String nillableField; private String privateField; private static String staticField; public String getNillableField() { return nillableField; } public void setNillableField(String nillableField) { this.nillableField = nillableField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyName.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class JsonbPropertyName { @JsonbProperty("fieldAnnotatedNameCustomized") private String fieldAnnotatedName; private String methodAnnotName; @JsonbProperty("fieldAnnotatedNameToOverride") private String fieldOverriddenWithMethodAnnot; public String getFieldAnnotatedName() { return fieldAnnotatedName; } public void setFieldAnnotatedName(String fieldAnnotatedName) { this.fieldAnnotatedName = fieldAnnotatedName; } @JsonbProperty("getterAnnotatedName") public String getMethodAnnotName() { return methodAnnotName; } @JsonbProperty("setterAnnotatedName") public void setMethodAnnotName(String methodAnnotName) { this.methodAnnotName = methodAnnotName; } @JsonbProperty("getterOverriddenName") public String getFieldOverriddenWithMethodAnnot() { return fieldOverriddenWithMethodAnnot; } @JsonbProperty("setterOverriddenName") public void setFieldOverriddenWithMethodAnnot(String fieldOverriddenWithMethodAnnot) { this.fieldOverriddenWithMethodAnnot = fieldOverriddenWithMethodAnnot; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNameCollision.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class JsonbPropertyNameCollision { private String pojoName; @JsonbProperty("pojoName") private String pojoNameCollision; public String getPojoName() { return pojoName; } public void setPojoName(String pojoName) { this.pojoName = pojoName; } public String getPojoNameCollision() { return pojoNameCollision; } public void setPojoNameCollision(String pojoNameCollision) { this.pojoNameCollision = pojoNameCollision; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNillable.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; /** * @author Roman Grigoriadi */ public class JsonbPropertyNillable { @JsonbProperty(nillable = true) private String nullField; @JsonbProperty(nillable = false) private String nillableOverride; public String getNullField() { return nullField; } public void setNullField(String nullField) { this.nullField = nullField; } @JsonbProperty(nillable = true) public String getNillableOverride() { return nillableOverride; } public void setNillableOverride(String nillableOverride) { this.nillableOverride = nillableOverride; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNumberFormat; import java.math.BigDecimal; import java.math.BigInteger; /** * @author Roman Grigoriadi */ @JsonbNumberFormat("0.0") public class NumberFormatPojo { @JsonbNumberFormat("00000000.000000") public BigDecimal bigDecimal; @JsonbNumberFormat("00000000") public BigInteger bigInteger; @JsonbNumberFormat("000.00000000") public Double aDouble; @JsonbNumberFormat("000.00000000") public Float aFloat; @JsonbNumberFormat("00000000000") public Long aLong; public Integer integer; @JsonbNumberFormat("00000") public Short aShort; @JsonbNumberFormat("000") public Byte aByte; private Double doubleGetterFormatted; private Double doubleSetterFormatted; @JsonbNumberFormat(value = "000.000", locale = "en-us") private Double doubleSetterAndPropertyFormatter; @JsonbNumberFormat("000.00000000") public Double getDoubleGetterFormatted() { return doubleGetterFormatted; } public void setDoubleGetterFormatted(Double doubleGetterFormatted) { this.doubleGetterFormatted = doubleGetterFormatted; } public Double getDoubleSetterFormatted() { return doubleSetterFormatted; } @JsonbNumberFormat(value = "000,000", locale = "da-da") public void setDoubleSetterFormatted(Double doubleSetterFormatted) { this.doubleSetterFormatted = doubleSetterFormatted; } public Double getDoubleSetterAndPropertyFormatter() { return doubleSetterAndPropertyFormatter; } @JsonbNumberFormat(value = "000,000", locale = "da-da") public void setDoubleSetterAndPropertyFormatter(Double doubleSetterAndPropertyFormatter) { this.doubleSetterAndPropertyFormatter = doubleSetterAndPropertyFormatter; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojoWithoutClassLevelFormatter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbNumberFormat; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class NumberFormatPojoWithoutClassLevelFormatter { private Double doubleGetterFormatted; private Double doubleSetterFormatted; @JsonbNumberFormat(value = "000.000", locale = "en-us") private Double doubleSetterAndPropertyFormatter; @JsonbNumberFormat("000.00000000") public Double getDoubleGetterFormatted() { return doubleGetterFormatted; } public void setDoubleGetterFormatted(Double doubleGetterFormatted) { this.doubleGetterFormatted = doubleGetterFormatted; } public Double getDoubleSetterFormatted() { return doubleSetterFormatted; } @JsonbNumberFormat(value = "000,000", locale = "da-da") public void setDoubleSetterFormatted(Double doubleSetterFormatted) { this.doubleSetterFormatted = doubleSetterFormatted; } public Double getDoubleSetterAndPropertyFormatter() { return doubleSetterAndPropertyFormatter; } @JsonbNumberFormat(value = "000,000", locale = "da-da") public void setDoubleSetterAndPropertyFormatter(Double doubleSetterAndPropertyFormatter) { this.doubleSetterAndPropertyFormatter = doubleSetterAndPropertyFormatter; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/ParameterNameTester.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import java.net.URI; /** * * @author David Kral */ public class ParameterNameTester { @JsonbProperty("string") public final String name; @JsonbProperty("someParam") public final String secondParam; @JsonbCreator public ParameterNameTester(@JsonbProperty("string") String name, @JsonbProperty("someParam") String secondParam) { this.name = name; this.secondParam = secondParam; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/RenamedPropertiesContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbProperty; public class RenamedPropertiesContainer { @JsonbProperty("first") private int intInstance; @JsonbProperty("second") private String stringInstance; @JsonbProperty("third") private long longInstance; public String getStringInstance() { return stringInstance; } public void setStringInstance(String stringInstance) { this.stringInstance = stringInstance; if (intInstance == 1) { intInstance = 2; } } public int getIntInstance() { return intInstance; } public void setIntInstance(int intInstance) { this.intInstance = intInstance; } public long getLongInstance() { return longInstance; } public void setLongInstance(long longInstance) { this.longInstance = longInstance; if (intInstance == 2) { intInstance = 3; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/TrimmedDatePojo.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; public class TrimmedDatePojo { @JsonbDateFormat(value = "yyyy.MM.dd") private Date date; @JsonbDateFormat(value = "yyyy.MM.dd") private Calendar calendar; @JsonbDateFormat(value = "yyyy.MM.dd") private LocalDateTime localDateTime; @JsonbDateFormat(value = "yyyy.MM.dd") private ZonedDateTime zonedDateTime; @JsonbDateFormat(value = "yyyy.MM.dd HH:ss") private ZonedDateTime zonedDateTimeHoursAndSeconds; @JsonbDateFormat(value = "yyyy.MM.dd N") private ZonedDateTime zonedDateTimeNanosOfDay; @JsonbDateFormat(value = "yyyy.MM.dd VV") private ZonedDateTime zonedDateTimeOverriddenZone; @JsonbDateFormat(value = "yyyy.MM.dd VV") private Instant zonedInstant; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public LocalDateTime getLocalDateTime() { return localDateTime; } public void setLocalDateTime(LocalDateTime localDateTime) { this.localDateTime = localDateTime; } public ZonedDateTime getZonedDateTime() { return zonedDateTime; } public void setZonedDateTime(ZonedDateTime zonedDateTime) { this.zonedDateTime = zonedDateTime; } public ZonedDateTime getZonedDateTimeNanosOfDay() { return zonedDateTimeNanosOfDay; } public void setZonedDateTimeNanosOfDay(ZonedDateTime zonedDateTimeNanosOfDay) { this.zonedDateTimeNanosOfDay = zonedDateTimeNanosOfDay; } public ZonedDateTime getZonedDateTimeHoursAndSeconds() { return zonedDateTimeHoursAndSeconds; } public void setZonedDateTimeHoursAndSeconds(ZonedDateTime zonedDateTimeHoursAndSeconds) { this.zonedDateTimeHoursAndSeconds = zonedDateTimeHoursAndSeconds; } public ZonedDateTime getZonedDateTimeOverriddenZone() { return zonedDateTimeOverriddenZone; } public void setZonedDateTimeOverriddenZone(ZonedDateTime zonedDateTimeOverriddenZone) { this.zonedDateTimeOverriddenZone = zonedDateTimeOverriddenZone; } public Instant getZonedInstant() { return zonedInstant; } public void setZonedInstant(Instant zonedInstant) { this.zonedInstant = zonedInstant; } public Calendar getCalendar() { return calendar; } public void setCalendar(Calendar calendar) { this.calendar = calendar; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/JsonbNillablePackageLevel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model.packagelevelannotations; /** * @author Roman Grigoriadi */ public class JsonbNillablePackageLevel { private String packageLevelNillableField; public String getPackageLevelNillableField() { return packageLevelNillableField; } public void setPackageLevelNillableField(String packageLevelNillableField) { this.packageLevelNillableField = packageLevelNillableField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/PackageLevelOverriddenWithClassLevel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.model.packagelevelannotations; import jakarta.json.bind.annotation.JsonbNillable; /** * @author Roman Grigoriadi */ @JsonbNillable(false) public class PackageLevelOverriddenWithClassLevel { private String nillableOverriddenField; public String getNillableOverriddenField() { return nillableOverriddenField; } public void setNillableOverriddenField(String nillableOverriddenField) { this.nillableOverriddenField = nillableOverriddenField; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/package-info.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /** * @author Roman Grigoriadi */ @JsonbNillable package org.eclipse.yasson.customization.model.packagelevelannotations; import jakarta.json.bind.annotation.JsonbNillable; ================================================ FILE: src/test/java/org/eclipse/yasson/customization/polymorphism/AnnotationPolymorphismTest.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.polymorphism; import java.time.LocalDate; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTypeInfo; import org.eclipse.yasson.Jsonbs; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; /** * Tests for verification of proper polymorphism handling based on annotation. */ public class AnnotationPolymorphismTest { public static final String ARRAY_EXPECTED = "[{\"@type\":\"dog\",\"isDog\":true},{\"@type\":\"cat\",\"isCat\":true}," + "{\"@type\":\"dog\",\"isDog\":true}]"; @Test public void testBasicSerialization() { Dog dog = new Dog(); assertThat(Jsonbs.defaultJsonb.toJson(dog), is("{\"@type\":\"dog\",\"isDog\":true}")); Cat cat = new Cat(); assertThat(Jsonbs.defaultJsonb.toJson(cat), is("{\"@type\":\"cat\",\"isCat\":true}")); } @Test public void testBasicDeserialization() { Animal dog = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"dog\",\"isDog\":false}", Animal.class); assertThat(dog, instanceOf(Dog.class)); assertThat(((Dog) dog).isDog, is(false)); Animal cat = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"cat\",\"isCat\":false}", Animal.class); assertThat(cat, instanceOf(Cat.class)); assertThat(((Cat) cat).isCat, is(false)); } @Test public void testExactTypeDeserialization() { Dog dog = Jsonbs.defaultJsonb.fromJson("{\"isDog\":false}", Dog.class); assertThat(dog.isDog, is(false)); dog = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"dog\", \"isDog\":false}", Dog.class); assertThat(dog.isDog, is(false)); } @Test public void testUnknownAliasDeserialization() { JsonbException exception = assertThrows(JsonbException.class, () -> Jsonbs.defaultJsonb.fromJson("{\"@type\":\"rat\",\"isDog\":false}", Animal.class)); assertThat(exception.getMessage(), startsWith("Unknown alias \"rat\" of the type org.eclipse.yasson.customization." + "polymorphism.AnnotationPolymorphismTest$Animal. Known aliases: [")); } @Test public void testUnknownAliasSerialization() { Rat rat = new Rat(); assertThat(Jsonbs.defaultJsonb.toJson(rat), is("{\"isRat\":true}")); } @Test public void testCreatorDeserialization() { SomeDateType creator = Jsonbs.defaultJsonb .fromJson("{\"@dateType\":\"constructor\",\"localDate\":\"26-02-2021\"}", SomeDateType.class); assertThat(creator, instanceOf(DateConstructor.class)); } @Test public void testArraySerialization() { Animal[] animals = new Animal[] {new Dog(), new Cat(), new Dog()}; assertThat(Jsonbs.defaultJsonb.toJson(animals), is(ARRAY_EXPECTED)); } @Test public void testArrayDeserialization() { Animal[] deserialized = Jsonbs.defaultJsonb.fromJson(ARRAY_EXPECTED, Animal[].class); assertThat(deserialized.length, is(3)); assertThat(deserialized[0], instanceOf(Dog.class)); assertThat(deserialized[1], instanceOf(Cat.class)); assertThat(deserialized[2], instanceOf(Dog.class)); } @JsonbTypeInfo({ @JsonbSubtype(alias = "dog", type = Dog.class), @JsonbSubtype(alias = "cat", type = Cat.class) }) public interface Animal { } public static class Dog implements Animal { public boolean isDog = true; } public static class Cat implements Animal { public boolean isCat = true; } public static class Rat implements Animal { public boolean isRat = true; } @JsonbTypeInfo(key = "@dateType", value = { @JsonbSubtype(alias = "constructor", type = DateConstructor.class) }) public interface SomeDateType { } public static final class DateConstructor implements SomeDateType { public LocalDate localDate; @JsonbCreator public DateConstructor(@JsonbProperty("localDate") @JsonbDateFormat(value = "dd-MM-yyyy", locale = "nl-NL") LocalDate localDate) { this.localDate = localDate; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.polymorphism; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTypeInfo; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * TODO javadoc */ public class MultiplePolymorphicInfoTest { private static final Jsonb JSONB = JsonbBuilder.create(); @Test public void testMultiplePolymorphicInfoPropertySerialization() { String expected = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}"; Labrador labrador = new Labrador(); assertThat(JSONB.toJson(labrador), is(expected)); } @Test public void testMultiplePolymorphicInfoPropertyDeserialization() { String json = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}"; assertThat(JSONB.fromJson(json, Labrador.class), instanceOf(Labrador.class)); } @Test public void testPolymorphicParentInstanceSerialization() { String expected = "{\"@type\":\"area\",\"name\":\"North America\",\"population\":600000000}"; Area northAmerica = new Area(); northAmerica.name = "North America"; northAmerica.population = 600000000; assertThat(JSONB.toJson(northAmerica), is(expected)); } @Test public void testPolymorphicParentInstanceDeserialization() { String json = "{\"@type\":\"area\",\"name\":\"North America\",\"population\":600000000}"; assertThat(JSONB.fromJson(json, Location.class), instanceOf(Area.class)); } @JsonbTypeInfo(key = "@something", value = { @JsonbSubtype(alias = "animal", type = Animal.class) }) public interface Something { } @JsonbTypeInfo(key = "@animal", value = { @JsonbSubtype(alias = "dog", type = Dog.class) }) public interface Animal extends Something { } @JsonbTypeInfo(key = "@dogRace", value = { @JsonbSubtype(alias = "labrador", type = Labrador.class) }) public interface Dog extends Animal { } public static class Labrador implements Dog { public boolean isLabrador = true; } @JsonbTypeInfo({ @JsonbSubtype(alias = "area", type = Area.class) }) public interface Location { } @JsonbTypeInfo(key = "@area", value = { @JsonbSubtype(alias = "city", type = City.class), @JsonbSubtype(alias = "state", type = State.class) }) public static class Area implements Location { public String name; public long population; } public static class City extends Area { public String state; } public static class State extends Area { public String capital; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java ================================================ /* * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.polymorphism; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTypeInfo; import org.eclipse.yasson.Jsonbs; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Tests for verification of proper polymorphism handling based on annotation. */ public class NestedPolymorphismTest { /** * 1st test for: https://github.com/eclipse-ee4j/yasson/issues/589 *

(deserialization of nested polymorphic and unmapped properties) */ @Test public void testNestedUnmappedProperty() { String json = "{\"inner\":{\"id\":123,\"@type\":\"derivationA\"," + "\"unmapped\":{\"x\":9,\"y\":[9,8,7]},\"name\":\"abc\"}}"; Outer obj = assertDoesNotThrow(() -> defaultJsonb.fromJson(json, Outer.class)); assertEquals(123L, obj.inner.id); assertEquals("abc", obj.inner.name); } // a base class @JsonbTypeInfo(key = "@type", value = @JsonbSubtype(type = InnerBase.class, alias = "derivationA")) public static class InnerBase { public Long id; public String name; } // derivation of the base class public class Derivation extends InnerBase {} // an arbitrary 'outer' root element public static class Outer { public InnerBase inner; } /** * 2nd test for: https://github.com/eclipse-ee4j/yasson/issues/589 *

(deserialization of multiple nested polymorphic properties) */ @Test public void testNestedDeserialization() { String json = "{\"@type\":\"Pets\",\"pet1\":{\"@type\":\"Cat\",\"name\":\"kitty\"}" + ",\"pet2\":{\"@type\":\"Dog\",\"name\":\"puppy\"}}"; final Animals animals = Jsonbs.defaultJsonb.fromJson(json, Animals.class); assertThat(animals, instanceOf(Pets.class)); assertNotNull(((Pets) animals).pet1, "Empty 'pet1' property"); assertEquals("kitty", ((Cat) ((Pets) animals).pet1).name, "First pet has invalid name"); assertNotNull(((Pets) animals).pet2, "Empty 'pet2' property"); assertThat("Invalid pet nr 2", ((Pets) animals).pet2, instanceOf(Dog.class)); } @JsonbTypeInfo(key = "@type", value = { @JsonbSubtype(alias = "Dog", type = Dog.class), @JsonbSubtype(alias = "Cat", type = Cat.class) }) public interface Pet { public String getType(); } public static class Dog implements Pet { public String name; @Override public String getType() { return "Dog"; } } public static class Cat implements Pet { public String name; @Override public String getType() { return "Cat"; } } @JsonbTypeInfo(key = "@type", value = { @JsonbSubtype(alias = "Pets", type = Pets.class), @JsonbSubtype(alias = "Fishes", type = Fishes.class) }) public interface Animals { } public static class Pets implements Animals { public Pet pet1; public Pet pet2; } public static class Fishes implements Animals { } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/JsonbTransientTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.customization.transients.models.*; import jakarta.json.bind.JsonbException; /** * @author Roman Grigoriadi */ public class JsonbTransientTest { @Test public void testJsonbTransientPropertySerialize() { JsonbTransientValue pojo = new JsonbTransientValue(); pojo.setPlainProperty("non transient"); pojo.setPropertyTransient("TRANSIENT"); pojo.setGetterTransient("Getter transient value"); pojo.setSetterTransient("Setter transient value"); pojo.setGetterAndPropertyTransient("Getter and property transient value"); pojo.setSetterAndPropertyTransient("Setter and property transient value"); pojo.setSetterAndGetterTransient("Setter and getter transient value"); pojo.setSetterAndGetterAndPropertyTransient("Setter and getter and property transient value"); assertEquals("{\"plainProperty\":\"non transient\",\"setterTransient\":\"Setter transient value\"}", defaultJsonb.toJson(pojo)); } @Test public void testJsonbTransientPropertyDeserialize() { JsonbTransientValue result = defaultJsonb.fromJson("{\"plainProperty\":\"plainProperty value\"," + "\"propertyTransient\":\"TRANSIENT\"," + "\"getterTransient\":\"Getter transient value\"," + "\"setterTransient\":\"Setter transient value\"," + "\"getterAndPropertyTransient\":\"Getter and property transient value\"," + "\"setterAndPropertyTransient\":\"Setter and property transient value\"," + "\"setterAndGetterTransient\":\"Setter and getter transient value\"," + "\"setterAndGetterAndPropertyTransient\":\"Setter and getter and property transient value\"" + "}" , JsonbTransientValue.class); assertEquals("plainProperty value", result.getPlainProperty()); assertNull(result.getPropertyTransient()); assertEquals("Getter transient value", result.getGetterTransient()); assertNull(result.getSetterTransient()); assertNull(result.getGetterAndPropertyTransient()); assertNull(result.getSetterAndPropertyTransient()); assertNull(result.getSetterAndGetterTransient()); assertNull(result.getSetterAndGetterAndPropertyTransient()); } @Test public void testTransientCollidesOnProperty() throws Exception { JsonbTransientCollisionOnProperty pojo = new JsonbTransientCollisionOnProperty(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientCollidesOnGetter() throws Exception { JsonbTransientCollisionOnGetter pojo = new JsonbTransientCollisionOnGetter(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientCollidesOnPropertyAndGetter() throws Exception { JsonbTransientCollisionOnPropertyAndGetter pojo = new JsonbTransientCollisionOnPropertyAndGetter(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientCollidesOnSetter() throws Exception { JsonbTransientCollisionOnSetter pojo = new JsonbTransientCollisionOnSetter(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientCollidesOnPropertyAndSetter() throws Exception { JsonbTransientCollisionOnPropertyAndSetter pojo = new JsonbTransientCollisionOnPropertyAndSetter(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientCollidesOnPropertyAndGetterAndSetter() throws Exception { JsonbTransientCollisionOnPropertyAndGetterAndSetter pojo = new JsonbTransientCollisionOnPropertyAndGetterAndSetter(); pojo.setTransientProperty("TRANSIENT"); try { defaultJsonb.toJson(pojo); fail(); } catch (JsonbException e) { assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property.")); } } @Test public void testTransientGetterPlusJsonbPropertyField() { assertThrows(JsonbException.class, () -> { TransientGetterPlusCustomizationAnnotatedFieldContainer pojo = new TransientGetterPlusCustomizationAnnotatedFieldContainer(); defaultJsonb.toJson(pojo); }); } @Test public void testTransientSetterPlusJsonbPropertyField() { assertThrows(JsonbException.class, () -> { TransientSetterPlusCustomizationAnnotatedFieldContainer pojo = new TransientSetterPlusCustomizationAnnotatedFieldContainer(); defaultJsonb.toJson(pojo); }); } @Test public void testTransientSetterplusJsonbPropertyGetter() { TransientSetterPlusCustomizationAnnotatedGetterContainer pojo = new TransientSetterPlusCustomizationAnnotatedGetterContainer(); assertEquals("{\"instance\":\"INSTANCE\"}", defaultJsonb.toJson(pojo)); } @Test public void testTransientGetterNoField() { TransientGetterNoField pojo = new TransientGetterNoField(); assertEquals("{}", defaultJsonb.toJson(pojo)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnGetter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Roman Grigoriadi */ public class JsonbTransientCollisionOnGetter { private String transientProperty; @JsonbTransient @JsonbProperty("custom_name") public String getTransientProperty() { return transientProperty; } public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnProperty.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbTransientCollisionOnProperty { @JsonbTransient @JsonbProperty("custom_name") private String transientProperty; public String getTransientProperty() { return transientProperty; } public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbTransientCollisionOnPropertyAndGetter { @JsonbTransient private String transientProperty; @JsonbProperty("custom_name") public String getTransientProperty() { return transientProperty; } public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetterAndSetter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbTransientCollisionOnPropertyAndGetterAndSetter { @JsonbTransient private String transientProperty; @JsonbProperty("custom_name") public String getTransientProperty() { return transientProperty; } @JsonbProperty("custom_name") public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndSetter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbTransientCollisionOnPropertyAndSetter { @JsonbTransient private String transientProperty; public String getTransientProperty() { return transientProperty; } @JsonbProperty("custom_name") public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnSetter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class JsonbTransientCollisionOnSetter { private String transientProperty; public String getTransientProperty() { return transientProperty; } @JsonbTransient @JsonbProperty("custom_name") public void setTransientProperty(String transientProperty) { this.transientProperty = transientProperty; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientValue.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbTransient; /** * @author Roman Grigoriadi */ public class JsonbTransientValue { private String plainProperty; @JsonbTransient public String publicTransient = "publicTransient"; @JsonbTransient private String transientFieldWithGetterNoSetter = "transientFieldWithGetterNoSetter"; @JsonbTransient private String propertyTransient; private String getterTransient; private String setterTransient; @JsonbTransient private String getterAndPropertyTransient; @JsonbTransient private String setterAndPropertyTransient; private String setterAndGetterTransient; @JsonbTransient private String setterAndGetterAndPropertyTransient; public String getPropertyTransient() { return propertyTransient; } public void setPropertyTransient(String propertyTransient) { this.propertyTransient = propertyTransient; } public String getPlainProperty() { return plainProperty; } public void setPlainProperty(String plainProperty) { this.plainProperty = plainProperty; } @JsonbTransient public String getGetterTransient() { return getterTransient; } public void setGetterTransient(String getterTransient) { this.getterTransient = getterTransient; } public String getSetterTransient() { return setterTransient; } @JsonbTransient public void setSetterTransient(String setterTransient) { this.setterTransient = setterTransient; } @JsonbTransient public String getGetterAndPropertyTransient() { return getterAndPropertyTransient; } public void setGetterAndPropertyTransient(String getterAndPropertyTransient) { this.getterAndPropertyTransient = getterAndPropertyTransient; } public String getSetterAndPropertyTransient() { return setterAndPropertyTransient; } @JsonbTransient public void setSetterAndPropertyTransient(String setterAndPropertyTransient) { this.setterAndPropertyTransient = setterAndPropertyTransient; } @JsonbTransient public String getSetterAndGetterTransient() { return setterAndGetterTransient; } @JsonbTransient public void setSetterAndGetterTransient(String setterAndGetterTransient) { this.setterAndGetterTransient = setterAndGetterTransient; } @JsonbTransient public String getSetterAndGetterAndPropertyTransient() { return setterAndGetterAndPropertyTransient; } @JsonbTransient public void setSetterAndGetterAndPropertyTransient(String setterAndGetterAndPropertyTransient) { this.setterAndGetterAndPropertyTransient = setterAndGetterAndPropertyTransient; } public String getTransientFieldWithGetterNoSetter() { return transientFieldWithGetterNoSetter; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterNoField.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbTransient; public class TransientGetterNoField { @JsonbTransient public String getFooString() { return ""; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterPlusCustomizationAnnotatedFieldContainer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; public class TransientGetterPlusCustomizationAnnotatedFieldContainer { @JsonbProperty("instance") private String instance; @JsonbTransient public String getInstance() { return instance; } public void setInstance(String instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedFieldContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; public class TransientSetterPlusCustomizationAnnotatedFieldContainer { @JsonbProperty("instance") private String instance; public String getInstance() { return instance; } @JsonbTransient public void setInstance(String instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedGetterContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.customization.transients.models; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; public class TransientSetterPlusCustomizationAnnotatedGetterContainer { private String instance = "INSTANCE"; @JsonbProperty("instance") public String getInstance() { return instance; } @JsonbTransient public void setInstance(String instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/EnumTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.collections.Language; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; public class EnumTest { @Test public void testEnumValue() { assertEquals("\"Russian\"", defaultJsonb.toJson(Language.Russian)); Language result = defaultJsonb.fromJson("\"Russian\"", Language.class); assertEquals(Language.Russian, result); } @Test public void testEnumInObject() { assertEquals("{\"value\":\"Russian\"}", defaultJsonb.toJson(new ScalarValueWrapper<>(Language.Russian))); ScalarValueWrapper result = defaultJsonb.fromJson("{\"value\":\"Russian\"}", new TestTypeToken>() { }.getType()); assertEquals(result.getValue(), Language.Russian); } @Test public void testEnumValueWithToStringOverriden() { assertEquals("\"HARD_BACK\"", defaultJsonb.toJson(Binding.HARD_BACK)); Binding result = defaultJsonb.fromJson("\"HARD_BACK\"", Binding.class); assertEquals(Binding.HARD_BACK, result); } @Test public void testEnumInObjectWithToStringOverriden() { assertEquals("{\"value\":\"HARD_BACK\"}", defaultJsonb.toJson(new ScalarValueWrapper<>(Binding.HARD_BACK))); ScalarValueWrapper result = defaultJsonb.fromJson("{\"value\":\"HARD_BACK\"}", new TestTypeToken>(){}.getType()); assertEquals(Binding.HARD_BACK, result.getValue()); } public enum Binding { HARD_BACK { @Override public String toString() { return "Hard Back"; } } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.time.*; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** * @author Roman Grigoriadi */ public class IJsonTest { private Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true)); @Test public void testStrictCalendar() { Calendar calendar = Calendar.getInstance(); calendar.clear(); calendar.set(1970, 0, 1, 0, 0, 0); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris")); String jsonString = jsonb.toJson(new ScalarValueWrapper<>(calendar)); assertEquals("{\"value\":\"1970-01-01T00:00:00Z+01:00\"}", jsonString); ScalarValueWrapper result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+01:00\"}", new TestTypeToken>() {}.getType()); assertEquals(calendar.toInstant(), result.getValue().toInstant()); } @Test public void testStrictDate() { Calendar calendar = Calendar.getInstance(); calendar.set(1970, 0, 1, 0, 0, 0); calendar.setTimeZone(TimeZone.getTimeZone("UTC")); calendar.clear(Calendar.MILLISECOND); String jsonString = jsonb.toJson(new ScalarValueWrapper<>(calendar.getTime())); assertTrue(jsonString.matches("\\{\"value\":\"1970-01-01T00:00:00Z\\+[0-9]{2}:[0-9]{2}\"}")); ScalarValueWrapper result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", new TestTypeToken>(){}.getType()); assertEquals(0, result.getValue().compareTo(calendar.getTime())); } @Test public void testStrictInstant() { Instant instant = LocalDateTime.of(2017, 3, 24, 12,0,0).toInstant(ZoneOffset.MIN); final String json = jsonb.toJson(new ScalarValueWrapper<>(instant)); assertEquals("{\"value\":\"2017-03-25T06:00:00Z+00:00\"}", json); ScalarValueWrapper result = jsonb.fromJson("{\"value\":\"2017-03-25T06:00:00Z+00:00\"}", new TestTypeToken>() {}.getType()); assertEquals(instant, result.getValue()); } @Test public void testLocalDate() { final LocalDate localDate = LocalDate.of(1970, 1, 1); final String json = jsonb.toJson(new ScalarValueWrapper<>(localDate)); assertEquals("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", json); ScalarValueWrapper result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", new TestTypeToken>() { }.getType()); assertEquals(localDate, result.getValue()); } @Test public void testLocalDateTime() { final LocalDateTime localDateTime = LocalDateTime.of(1970, 1, 1, 1, 1, 1); final String json = jsonb.toJson(new ScalarValueWrapper<>(localDateTime)); assertEquals("{\"value\":\"1970-01-01T01:01:01Z+00:00\"}", json); ScalarValueWrapper result = jsonb.fromJson("{\"value\":\"1970-01-01T01:01:01Z+00:00\"}", new TestTypeToken>() { }.getType()); assertEquals(localDateTime, result.getValue()); } @Test public void testDuration() { Duration duration = Duration.ofDays(1).plus(Duration.ofHours(1)).plus(Duration.ofSeconds(1)); final String json = jsonb.toJson(new ScalarValueWrapper<>(duration)); assertEquals("{\"value\":\"PT25H1S\"}", json); ScalarValueWrapper result = jsonb.fromJson(json, new TestTypeToken>() { }.getType()); assertEquals(duration, result.getValue()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/anonymous/AnonymousClassTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.anonymous; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; /** * This class contains tests for marshalling/unmarshalling anonymous classes. * * @author Dmitry Kornilov */ public class AnonymousClassTest { @Test public void testMarshallInnerClass() { assertEquals("{\"anonymousField\":\"anonymousValue\",\"id\":1,\"name\":\"pojoName\"}", defaultJsonb.toJson(new InnerPojo() { public String anonymousField = "anonymousValue"; @Override public Integer getId() { return 1; } @Override public String getName() { return "pojoName"; } })); } @Test public void testMarshallOuterClass() { assertEquals("{\"id\":1,\"anonymousField\":\"anonymousValue\"}", defaultJsonb.toJson(new OuterPojo() { public String anonymousField = "anonymousValue"; })); } public static class InnerPojo { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/anonymous/OuterPojo.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.anonymous; /** * Test class. * * @author Dmitry Kornilov */ public class OuterPojo { private Integer id = 1; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/BasicTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; /** * Default mapping primitives tests. * * @author Dmitry Kornilov */ public class BasicTest { @Test public void testMarshallEscapedString() { assertEquals("[\" \\\\ \\\" / \\f\\b\\r\\n\\t 9\"]", bindingJsonb.toJson(new String[] {" \\ \" / \f\b\r\n\t \u0039"})); } @Test public void testMarshallWriter() { Writer writer = new StringWriter(); bindingJsonb.toJson(new Long[]{5L}, writer); assertEquals("[5]", writer.toString()); } @Test public void testDoubleWriter() throws Exception { Writer writer = new StringWriter(); writer.write("{"); bindingJsonb.toJson("Hello", writer); writer.write(","); bindingJsonb.toJson("World", writer); writer.write("}"); assertEquals("{\"Hello\",\"World\"}", writer.toString()); } @Test public void testMarshallOutputStream() throws IOException { try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { bindingJsonb.toJson(new Long[]{5L}, baos); assertEquals("[5]", baos.toString("UTF-8")); } } @Test public void testObjectSerialization() { final String val = bindingJsonb.toJson(new Object()); assertEquals("{}", val); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/BooleanTest.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import org.junit.jupiter.api.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.defaultmapping.basic.model.BooleanModel; /** * Tests serialization and deserialization of boolean values. * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class BooleanTest { @Test public void testBooleanSerialization() throws Exception { BooleanModel booleanModel = new BooleanModel(true, false); String expected = "{\"field1\":true,\"field2\":false}"; assertEquals(expected, defaultJsonb.toJson(booleanModel)); } @Test public void testBooleanDeserializationFromBooleanAsStringValue() throws Exception { BooleanModel booleanModel = defaultJsonb.fromJson("{\"field1\":\"true\",\"field2\":\"true\"}", BooleanModel.class); assertEquals(true, booleanModel.field1); assertEquals(true, booleanModel.field2); } @Test public void testBooleanDeserializationFromBooleanRawValue() throws Exception { BooleanModel booleanModel = defaultJsonb.fromJson("{\"field1\":false,\"field2\":false}", BooleanModel.class); assertThat(booleanModel.field1, is(false)); assertThat(booleanModel.field2, is(false)); } @Test public void testNakedBooleans() { assertEquals(true, defaultJsonb.fromJson("true", boolean.class)); assertEquals(true, defaultJsonb.fromJson("true", Boolean.class)); assertEquals(false, defaultJsonb.fromJson("false", boolean.class)); assertEquals(false, defaultJsonb.fromJson("false", Boolean.class)); assertEquals("true", defaultJsonb.toJson(true, boolean.class)); assertEquals("false", defaultJsonb.toJson(Boolean.FALSE, Boolean.class)); } //Fix for issue #390 @Test public void testBooleanArrays() { assertArrayEquals(new boolean[] {true, false}, defaultJsonb.fromJson("[true, false]", boolean[].class)); assertArrayEquals(new Boolean[] {true, false}, defaultJsonb.fromJson("[true, false]", Boolean[].class)); assertEquals("[true,false]", defaultJsonb.toJson(new boolean[] {true, false})); assertEquals("[true,false]", defaultJsonb.toJson(new Boolean[] {true, false})); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import static org.eclipse.yasson.Assertions.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.basic.model.BigDecimalInNumber; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import jakarta.json.Json; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonWriter; import jakarta.json.stream.JsonGenerator; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; import java.util.Collections; import java.util.Map; /** * @author Roman Grigoriadi */ public class NumberTest { @Test public void testSerializeFloat() { final String json = defaultJsonb.toJson(0.35f); assertEquals("0.35", json); Float result = defaultJsonb.fromJson("0.35", Float.class); assertEquals((Float) .35f, result); } @Test public void testBigDecimalMarshalling() { String jsonString = defaultJsonb.toJson(new BigDecimal("0.10000000000000001")); assertEquals("0.10000000000000001", jsonString); jsonString = defaultJsonb.toJson(new BigDecimal("0.1000000000000001")); assertEquals("0.1000000000000001", jsonString); BigDecimal result = defaultJsonb.fromJson("0.10000000000000001", BigDecimal.class); assertEquals(new BigDecimal("0.10000000000000001"), result); result = defaultJsonb.fromJson("0.100000000000000001", BigDecimal.class); assertEquals(new BigDecimal("0.100000000000000001"), result); } @Test public void testBigDecimalIEEE748() { String jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740991")); assertEquals("9007199254740991", jsonString); jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740992")); assertEquals("9007199254740992", jsonString); jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740991.1")); assertEquals("9007199254740991.1", jsonString); jsonString = defaultJsonb.toJson(new BigDecimal(new BigInteger("1"), -400)); assertEquals(new BigDecimal(new BigInteger("1"), -400).toString(), jsonString); } @Test public void testBigIntegerIEEE748() { String jsonString = defaultJsonb.toJson(new BigInteger("9007199254740991")); assertEquals("9007199254740991", jsonString); jsonString = defaultJsonb.toJson(new BigInteger("9007199254740992")); assertEquals("9007199254740992", jsonString); } @Test public void testBigDecimalInNumber() { BigDecimalInNumber testValueQuoted = new BigDecimalInNumber() {{setBigDecValue(new BigDecimal("9007199254740992"));}}; BigDecimalInNumber testValueUnQuoted = new BigDecimalInNumber() {{setBigDecValue(new BigDecimal("9007199254740991"));}}; String jsonString = defaultJsonb.toJson(testValueQuoted); assertEquals("{\"bigDecValue\":9007199254740992}", jsonString); jsonString = defaultJsonb.toJson(testValueUnQuoted); assertEquals("{\"bigDecValue\":9007199254740991}", jsonString); BigDecimalInNumber result = defaultJsonb.fromJson("{\"bigDecValue\":9007199254740992}", BigDecimalInNumber.class); assertEquals(testValueQuoted.getBigDecValue(), result.getBigDecValue()); result = defaultJsonb.fromJson("{\"bigDecValue\":9007199254740991}", BigDecimalInNumber.class); assertEquals(testValueUnQuoted.getBigDecValue(), result.getBigDecValue()); } @Test public void testBigDecimalWrappedMarshalling() { String jsonString = defaultJsonb.toJson(new ScalarValueWrapper<>(new BigDecimal("0.1000000000000001"))); assertEquals("{\"value\":0.1000000000000001}", jsonString); jsonString = defaultJsonb.toJson(new ScalarValueWrapper<>(new BigDecimal("0.10000000000000001"))); assertEquals("{\"value\":0.10000000000000001}", jsonString); ScalarValueWrapper result = defaultJsonb.fromJson("{\"value\":0.1000000000000001}", new TestTypeToken>(){}.getType()); assertEquals(new BigDecimal("0.1000000000000001"), result.getValue()); result = defaultJsonb.fromJson("{\"value\":0.10000000000000001}", new TestTypeToken>(){}.getType()); assertEquals(new BigDecimal("0.10000000000000001"), result.getValue()); } @Test public void testBigDecimalCastedToNumber() { String jsonString = defaultJsonb.toJson(new Object() { public Number number = new BigDecimal("0.10000000000000001"); }); assertEquals("{\"number\":0.10000000000000001}", jsonString); jsonString = defaultJsonb.toJson(new Object() { public Number number = new BigDecimal("0.1000000000000001"); }); assertEquals("{\"number\":0.1000000000000001}", jsonString); } @Test public void testLongIEEE748() { // 9007199254740991L Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1; Long upperJsUnsafeValue = maxJsSafeValue + 1; String json = defaultJsonb.toJson(maxJsSafeValue); assertEquals("9007199254740991", json); Long deserialized = defaultJsonb.fromJson(json, Long.class); assertEquals(Long.valueOf("9007199254740991"), deserialized); json = defaultJsonb.toJson(upperJsUnsafeValue); assertEquals("9007199254740992", json); deserialized = defaultJsonb.fromJson(json, Long.class); assertEquals(Long.valueOf("9007199254740992"), deserialized); Long minJsSafeValue = Math.negateExact(maxJsSafeValue); Long lowerJsUnsafeValue = minJsSafeValue - 1; json = defaultJsonb.toJson(minJsSafeValue); assertEquals("-9007199254740991", json); deserialized = defaultJsonb.fromJson(json, Long.class); assertEquals(Long.valueOf("-9007199254740991"), deserialized); json = defaultJsonb.toJson(lowerJsUnsafeValue); assertEquals("-9007199254740992", json); deserialized = defaultJsonb.fromJson(json, Long.class); assertEquals(Long.valueOf("-9007199254740992"), deserialized); } /** * Tests that JSON-P RI itself does no big number (out of IEEE 754 quotation). * This is why it is now must be done in Yasson to match the JSONB spec. */ @Test public void testJsonpBigNumber() { StringWriter w = new StringWriter(); JsonGenerator generator = Json.createGenerator(w); Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1; Long upperJsUnsafeValue = Long.MAX_VALUE; generator.writeStartObject(); generator.write("safeLongValue", maxJsSafeValue); generator.write("unsafeLongValue", upperJsUnsafeValue); generator.write("safeBigDecimalValue", BigDecimal.TEN); generator.write("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue)); generator.writeEnd(); generator.close(); assertEquals("{" + "\"safeLongValue\":9007199254740991," + "\"unsafeLongValue\":9223372036854775807," + "\"safeBigDecimalValue\":10," + "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString()); w = new StringWriter(); JsonWriter writer = Json.createWriter(w); JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); objectBuilder.add("safeLongValue", maxJsSafeValue); objectBuilder.add("unsafeLongValue", upperJsUnsafeValue); objectBuilder.add("safeBigDecimalValue", BigDecimal.valueOf(maxJsSafeValue)); objectBuilder.add("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue)); JsonObject build = objectBuilder.build(); writer.write(build); writer.close(); assertEquals("{" + "\"safeLongValue\":9007199254740991," + "\"unsafeLongValue\":9223372036854775807," + "\"safeBigDecimalValue\":9007199254740991," + "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString()); } public static class NumberContainer { public Double doubleProp; public Collection collectionProp; public Map mapProp; } @Test public void testSerializeInvalidDouble() { shouldFail(() -> defaultJsonb.toJson(Double.POSITIVE_INFINITY)); NumberContainer obj = new NumberContainer(); obj.doubleProp = Double.POSITIVE_INFINITY; shouldFail(() -> defaultJsonb.toJson(obj), msg -> msg.contains("doubleProp") && msg.contains("NumberContainer")); } @Test public void testSerializeInvalidDoubleCollection() { NumberContainer obj = new NumberContainer(); obj.collectionProp = Collections.singleton(Double.POSITIVE_INFINITY); shouldFail(() -> defaultJsonb.toJson(obj), msg -> msg.contains("collectionProp") && msg.contains("NumberContainer")); } @Test public void testSerializeInvalidDoubleMap() { NumberContainer obj = new NumberContainer(); obj.mapProp = Collections.singletonMap("doubleKey", Double.POSITIVE_INFINITY); shouldFail(() -> defaultJsonb.toJson(obj), msg -> msg.contains("mapProp") && msg.contains("NumberContainer")); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java ================================================ /* * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import java.net.URI; import java.time.Instant; import java.util.HashMap; import java.util.HashSet; import java.util.Objects; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; import org.jboss.weld.exceptions.IllegalStateException; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Tests to verify that read-only properties (properties with no field or setter) * are ignored when deserializing and that write-only properties (properties with * no field or getter) are ignored when serializing. */ public class PropertyMismatchTest { public static class CollectionGetterOnly { public HiddenCtorCollection getDataCollection() { throw new IllegalStateException("Get was called"); } public HiddenCtorMap getDataMap() { throw new IllegalStateException("Get was called"); } public HiddenCtorType[] getDataArray() { throw new IllegalStateException("Get was called"); } public HiddenCtorType getData() { throw new IllegalStateException("Get was called"); } } public static class CollectionSetterOnly { public void setDataCollection(HiddenCtorCollection set) { throw new IllegalStateException("Set was called"); } public void setDataMap(HiddenCtorMap map) { throw new IllegalStateException("Set was called"); } public void setDataArray(HiddenCtorType[] arr) { throw new IllegalStateException("Set was called"); } public void setData(HiddenCtorType obj) { throw new IllegalStateException("Set was called"); } } public static class HiddenCtorCollection extends HashSet { private static final long serialVersionUID = -2254550505591024068L; private HiddenCtorCollection() { throw new IllegalStateException("Object should not be initialized!"); } } public static class HiddenCtorMap extends HashMap { private static final long serialVersionUID = -3042588327575185446L; private HiddenCtorMap() { throw new IllegalStateException("Object should not be initialized!"); } } public static class HiddenCtorType { private HiddenCtorType() { throw new IllegalStateException("Object should not be initialized!"); } } /** * When deserializing a JSON, a property without a setter should be ignored as a property */ @Test public void testGetterOnly() { String jsonCollection = "{\"dataCollection\": [\"foo\"], " + "\"dataMap\": { \"foo\": \"bar\" }, " + "\"dataArray\": [\"foo\"], " + "\"data\": \"foo\" }"; CollectionGetterOnly collection = defaultJsonb.fromJson(jsonCollection, CollectionGetterOnly.class); // Don't need to verify resulting object (except that it is non-null) // because if any getters or ctors were called, we would get an ISE assertNotNull(collection); } /** * When serializing an object, a property without a getter should be ignored as a property */ @Test public void testSetterOnly() { CollectionSetterOnly obj = new CollectionSetterOnly(); String json = defaultJsonb.toJson(obj); assertEquals("{}", json); } public static class PropertyTypeMismatch { @JsonbTransient public Instant internalInstantProperty; private String foo; public int getFoo() { return foo.length(); } public void setFoo(Instant instant) { this.foo = instant.toString(); this.internalInstantProperty = instant; } } @Test public void testTransientAndPropertyAnnotationMerge() { TransientAndPropertyAnnotationMerge object = new TransientAndPropertyAnnotationMerge(); String expected = "{\"number\":\"http://localhost/\"}"; String json = defaultJsonb.toJson(object); assertEquals(expected, json); TransientAndPropertyAnnotationMerge deserialized = defaultJsonb.fromJson(expected, TransientAndPropertyAnnotationMerge.class); assertEquals(object, deserialized); } public static class TransientAndPropertyAnnotationMerge { @JsonbTransient private Integer number; @JsonbProperty("number") private URI someLink; public TransientAndPropertyAnnotationMerge() { number = -1; someLink = URI.create("http://localhost/"); } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } public URI getSomeLink() { return someLink; } public void setSomeLink(URI someLink) { this.someLink = someLink; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TransientAndPropertyAnnotationMerge that = (TransientAndPropertyAnnotationMerge) o; return Objects.equals(number, that.number) && Objects.equals(someLink, that.someLink); } @Override public int hashCode() { return Objects.hash(number, someLink); } } /** * Test that properties of the same name with different * field/getter/setter types behave properly and that we don't * assume they are all equal */ @Test public void testPropertyTypesMismatch() { PropertyTypeMismatch obj = new PropertyTypeMismatch(); Instant now = Instant.now(); obj.setFoo(now); String json = defaultJsonb.toJson(obj); assertEquals("{\"foo\":" + now.toString().length() + "}", json); PropertyTypeMismatch after = defaultJsonb.fromJson("{\"foo\":\"" + now.toString() + "\"}", PropertyTypeMismatch.class); assertEquals(obj.getFoo(), after.getFoo()); assertEquals(obj.internalInstantProperty, after.internalInstantProperty); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/SingleValueTest.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.internal.JsonBindingBuilder; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import java.math.BigDecimal; import java.math.BigInteger; /** * @author David Kral */ public class SingleValueTest { @Test public void testMarshallPrimitives() { // String assertEquals("\"some_string\"", bindingJsonb.toJson("some_string")); // Character assertEquals("\"\uFFFF\"", bindingJsonb.toJson('\uFFFF')); // Byte assertEquals("1", bindingJsonb.toJson((byte)1)); // Short assertEquals("1", bindingJsonb.toJson((short)1)); // Integer assertEquals("1", bindingJsonb.toJson(1)); // Long assertEquals("5", bindingJsonb.toJson(5L)); // Float assertEquals("1.2", bindingJsonb.toJson(1.2f)); // Double assertEquals("1.2", bindingJsonb.toJson(1.2)); // BigInteger assertEquals("1", bindingJsonb.toJson(new BigInteger("1"))); // BigDecimal assertEquals("1.2", bindingJsonb.toJson(new BigDecimal("1.2"))); // Number assertEquals("1.2", bindingJsonb.toJson(1.2)); // Boolean true assertEquals("true", bindingJsonb.toJson(true)); // Boolean false assertEquals("false", bindingJsonb.toJson(false)); assertEquals("1", bindingJsonb.toJson(1)); // null assertEquals("null", bindingJsonb.toJson(null)); } @Test public void testSingleValue() { assertEquals("5", bindingJsonb.toJson(5)); Jsonb jsonb = new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true)).build(); try { jsonb.toJson(5); fail(); } catch (JsonbException exception){ assertEquals(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE), exception.getMessage()); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/UnqualifiedPropertiesTest.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; public class UnqualifiedPropertiesTest { public static class Widget { public long now; public String getFoo() { return "foo"; } public String getBar(final int baz) { return "bar" + baz; } public boolean isPositive(int num) { return num > 0; } public Widget setNow() { now = 1511576115722L; return this; } } @Test public void testGetWithArgs() { assertEquals("{\"foo\":\"foo\",\"now\":0}", defaultJsonb.toJson(new Widget())); } @Test public void testSetWithNoArgs() { assertEquals("{\"foo\":\"foo\",\"now\":1511576115722}", defaultJsonb.toJson(new Widget().setNow())); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BigDecimalInNumber.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic.model; /** * Encapsulates Number type of field so the serialization and deserialization of number can be tested * * @author David Kral */ public class BigDecimalInNumber { private Number bigDecValue; public BigDecimalInNumber(){ } public Number getBigDecValue() { return bigDecValue; } public void setBigDecValue(Number bigDecValue) { this.bigDecValue = bigDecValue; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BooleanModel.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.basic.model; /** * Encapsulates different types of boolean values as a field so that the boolean value's serialization and * deserialization could be tested. * * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com) */ public class BooleanModel { public Boolean field1; public boolean field2; public BooleanModel() { } public BooleanModel(boolean field1, Boolean field2) { this.field2 = field2; this.field1 = field1; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/collections/ArrayTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.collections; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Roman Grigoriadi */ public class ArrayTest { @Test public void testStringArray() { String[] stringArray = new String[3]; stringArray[0] = "first"; stringArray[1] = "second"; stringArray[2] = "third"; String expected = "[\"first\",\"second\",\"third\"]"; assertEquals(expected, nullableJsonb.toJson(stringArray)); String[] result = nullableJsonb.fromJson(expected, stringArray.getClass()); assertEquals("first", result[0]); assertEquals("second", result[1]); assertEquals("third", result[2]); } @Test public void testObjectArrayOfObjects() { Object[] objectArray = new Object[2]; objectArray[0] = new KeyValue("first"); objectArray[1] = new KeyValue("second"); String expected = "[{\"field\":\"first\"},{\"field\":\"second\"}]"; assertEquals(expected, nullableJsonb.toJson(objectArray)); Object[] result = nullableJsonb.fromJson(expected, objectArray.getClass()); assertEquals(HashMap.class, result[0].getClass()); assertEquals(HashMap.class, result[1].getClass()); assertEquals("first", ((Map) result[0]).get("field")); assertEquals("second", ((Map) result[1]).get("field")); } @Test public void testListOfArrays() { List listOfArrays = new ArrayList<>(); String[] stringArray = new String[2]; stringArray[0] = "first"; stringArray[1] = "second"; listOfArrays.add(stringArray); listOfArrays.add(stringArray); String expected = "[[\"first\",\"second\"],[\"first\",\"second\"]]"; assertEquals(expected, nullableJsonb.toJson(listOfArrays)); List result = nullableJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals("first", result.get(0)[0]); assertEquals("second", result.get(0)[1]); assertEquals("first", result.get(1)[0]); assertEquals("second", result.get(1)[1]); } @Test public void testMultidimensionalArrays() { String[][] multi = new String[2][2]; multi[0][0] = "[0],[0]"; multi[0][1] = "[0],[1]"; multi[1][0] = "[1],[0]"; multi[1][1] = "[1],[1]"; String expected = "[[\"[0],[0]\",\"[0],[1]\"],[\"[1],[0]\",\"[1],[1]\"]]"; assertEquals(expected, nullableJsonb.toJson(multi)); String[][] result = nullableJsonb.fromJson(expected, multi.getClass()); assertEquals("[0],[0]", result[0][0]); assertEquals("[0],[1]", result[0][1]); assertEquals("[1],[0]", result[1][0]); assertEquals("[1],[1]", result[1][1]); } @Test public void testDeserializeJsonArrayIntoObject() { String json = "[\"first\",\"second\",\"third\"]"; Object result = nullableJsonb.fromJson(json, Object.class); assertTrue(result instanceof List); assertEquals("first", ((List)result).get(0)); assertEquals("second", ((List)result).get(1)); assertEquals("third", ((List)result).get(2)); } @Test public void testDeserializeJsonObjectIntoListOfMaps() { String json = "[{\"first\":1,\"second\":10}]"; Object result = nullableJsonb.fromJson(json, List.class); assertTrue(result instanceof List); assertEquals(BigDecimal.ONE, ((Map) ((List) result).get(0)).get("first")); assertEquals(BigDecimal.TEN, ((Map) ((List) result).get(0)).get("second")); } @Test public void testUnmarshallMapWithArrayValue() { Map arrayValueMap = new HashMap<>(); String[] strings = new String[2]; strings[0] = "zero"; strings[1] = "one"; arrayValueMap.put("first", strings); String expected = "{\"first\":[\"zero\",\"one\"]}"; assertEquals(expected, nullableJsonb.toJson(arrayValueMap)); Map result = nullableJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals("zero", result.get("first")[0]); assertEquals("one", result.get("first")[1]); } @Test public void testArrayOfNulls() { String[] nulls = new String[2]; String expected = "[null,null]"; assertEquals(expected, nullableJsonb.toJson(nulls)); String[] result = nullableJsonb.fromJson(expected, nulls.getClass()); assertTrue(result.length == 2); assertNull(result[0]); assertNull(result[1]); Integer ints[] = new Integer[2]; assertEquals(expected, nullableJsonb.toJson(ints)); } @Test public void testByteArray() { byte[] byteArr = {-128, 127}; assertEquals("[-128,127]", nullableJsonb.toJson(byteArr)); assertArrayEquals(byteArr, nullableJsonb.fromJson("[-128, 127]", byte[].class)); } @Test public void testCharArray() { char[] charArr = {'a', 'b', 'c'}; assertEquals("[\"a\",\"b\",\"c\"]", nullableJsonb.toJson(charArr)); assertArrayEquals(charArr, nullableJsonb.fromJson("[\"a\",\"b\",\"c\"]", char[].class)); } @Test public void testShortArray() { short[] shortArr = {-128, 127}; assertEquals("[-128,127]", nullableJsonb.toJson(shortArr)); assertArrayEquals(shortArr, nullableJsonb.fromJson("[-128, 127]", short[].class)); } @Test public void testIntArray() { int[] intArr = {-128, 127}; assertEquals("[-128,127]", nullableJsonb.toJson(intArr)); assertArrayEquals(intArr, nullableJsonb.fromJson("[-128, 127]", int[].class)); } @Test public void testLongArray() { long[] longArr = {-128, 127}; assertEquals("[-128,127]", nullableJsonb.toJson(longArr)); assertArrayEquals(longArr, nullableJsonb.fromJson("[-128, 127]", long[].class)); } @Test public void testFloatArray() { float[] floatArr = {-128, 127}; assertEquals("[-128.0,127.0]", nullableJsonb.toJson(floatArr)); assertArrayEquals(floatArr, nullableJsonb.fromJson("[-128.0, 127.0]", float[].class), 0f); } @Test public void testDoubleArray() { double[] doubleArr = {-128, 127}; assertEquals("[-128.0,127.0]", nullableJsonb.toJson(doubleArr)); assertArrayEquals(doubleArr, nullableJsonb.fromJson("[-128.0, 127.0]", double[].class), 0d); } public static class KeyValue { public String field; public KeyValue(String field) { this.field = field; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.collections; import static org.eclipse.yasson.Jsonbs.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Deque; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.Circle; import org.junit.jupiter.api.Test; /** * Default mapping arrays/collections/enums tests. * * @author Dmitry Kornilov */ public class CollectionsTest { @Test public void testMarshallCollection() { final Collection collection = Arrays.asList(1, 2, 3); assertEquals("[1,2,3]", nullableJsonb.toJson(collection)); } @Test public void testMarshallMap() { Map stringIntegerMap = new LinkedHashMap<>(); stringIntegerMap.put("1",1); stringIntegerMap.put("2",2); stringIntegerMap.put("3",3); assertEquals("{\"1\":1,\"2\":2,\"3\":3}", nullableJsonb.toJson(stringIntegerMap)); assertEquals(stringIntegerMap, nullableJsonb.fromJson("{\"1\":1,\"2\":2,\"3\":3}", new LinkedHashMap(){}.getClass().getGenericSuperclass())); System.out.println(); } @Test public void testMarshallMapWithNulls() { Map mapWithNulls = new LinkedHashMap<>(); mapWithNulls.put("key1",null); mapWithNulls.put("key2",null); mapWithNulls.put("key3",null); assertEquals("{\"key1\":null,\"key2\":null,\"key3\":null}", nullableJsonb.toJson(mapWithNulls)); } @Test public void testListOfNumbers() { List numberList = new ArrayList<>(); numberList.add(1L); numberList.add(2f); numberList.add(10); String result = nullableJsonb.toJson(numberList, new TestTypeToken>(){}.getType()); } @Test public void testListOfListsOfStrings() { List> listOfListsOfStrings = new ArrayList<>(); for(int i = 0; i < 10; i++) { List stringList = new ArrayList<>(); stringList.add("first"); stringList.add("second"); stringList.add("third"); listOfListsOfStrings.add(stringList); } final String expected = "[[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"]]"; assertEquals(expected, nullableJsonb.toJson(listOfListsOfStrings)); assertEquals(listOfListsOfStrings, nullableJsonb.fromJson(expected, new ArrayList>(){}.getClass().getGenericSuperclass())); } @Test public void listOfMapsOfListsOfMaps() { List>>> listOfMapsOfListsOfMaps = new ArrayList<>(); for(int i = 0; i < 3; i++) { Map>> mapOfListsOfMap = new HashMap<>(); for(int j = 0; j < 3; j++) { List> listOfMaps = new ArrayList<>(); for(int k = 0; k < 3; k++) { Map stringIntegerMap = new HashMap<>(); stringIntegerMap.put("first", 1); stringIntegerMap.put("second", 2); stringIntegerMap.put("third", 3); listOfMaps.add(stringIntegerMap); } mapOfListsOfMap.put(String.valueOf(j), listOfMaps); } listOfMapsOfListsOfMaps.add(mapOfListsOfMap); } String expected = "[{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]}]"; assertEquals(expected, nullableJsonb.toJson(listOfMapsOfListsOfMaps)); ArrayList>>> result = nullableJsonb.fromJson(expected, new TestTypeToken>>>>(){}.getType()); assertEquals(listOfMapsOfListsOfMaps, result); } @Test public void testAnyCollection() { final Deque deque = new ArrayDeque<>(); deque.add("dequeueFirst"); deque.add("dequeueSecond"); String expected = "[\"dequeueFirst\",\"dequeueSecond\"]"; assertEquals(expected, nullableJsonb.toJson(deque)); Deque dequeueResult = nullableJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals("dequeueFirst", dequeueResult.getFirst()); assertEquals("dequeueSecond", dequeueResult.getLast()); LinkedHashSet linkedHashSet = new LinkedHashSet<>(); linkedHashSet.add("setFirst"); linkedHashSet.add("setSecond"); expected = "[\"setFirst\",\"setSecond\"]"; assertEquals(expected, nullableJsonb.toJson(linkedHashSet)); LinkedHashSet linkedHashSetResult = nullableJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); Iterator iterator = linkedHashSetResult.iterator(); assertEquals("setFirst", iterator.next()); assertEquals("setSecond", iterator.next()); LinkedList linkedList = new LinkedList<>(); linkedList.add("listFirst"); linkedList.add("listSecond"); expected = "[\"listFirst\",\"listSecond\"]"; assertEquals(expected, nullableJsonb.toJson(linkedList)); LinkedList linkedListResult = nullableJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); iterator = linkedListResult.iterator(); assertEquals("listFirst", iterator.next()); assertEquals("listSecond", iterator.next()); } @Test @SuppressWarnings("unchecked") public void testMarshallArray() { //support of arrays of types that JSON Binding is able to serialize //Byte[], Short[], Integer[] Long[], Float[], Double[], BigInteger[], BigDecimal[], Number[] //Object[], JsonArray[], JsonObject[], JsonStructure[] //String[], Character[] //byte[], short[], int[], long[], float[], double[], char[], boolean[] //java.net.URL[], java.net.URI[] //Map[], Collection[], other collections ... //enum, EnumSet, EnumMap //support of multidimensional arrays final Byte[] byteArray = {1, 2, 3}; assertEquals("[1,2,3]", nullableJsonb.toJson(byteArray)); final Integer[] integerArray = {1, 2, 3}; assertEquals("[1,2,3]", nullableJsonb.toJson(integerArray)); final String[] stringArray = {"first", "second", "third"}; assertEquals("[\"first\",\"second\",\"third\"]", nullableJsonb.toJson(stringArray)); Character[] charArr = {'a', 'b', 'c'}; assertEquals("[\"a\",\"b\",\"c\"]", nullableJsonb.toJson(charArr)); final byte[] bytePrimitivesArray = {1, 2, 3}; assertEquals("[1,2,3]", nullableJsonb.toJson(bytePrimitivesArray)); final int[] intArray = {1, 2, 3}; assertEquals("[1,2,3]", nullableJsonb.toJson(intArray)); final String[][] stringMultiArray = {{"first", "second"},{"third", "fourth"}}; assertEquals("[[\"first\",\"second\"],[\"third\",\"fourth\"]]", nullableJsonb.toJson(stringMultiArray)); final Map[][] mapMultiArray = new LinkedHashMap[2][2]; mapMultiArray[0][0] = new LinkedHashMap<>(1); mapMultiArray[0][0].put("0", 0); mapMultiArray[0][1] = new LinkedHashMap<>(1); mapMultiArray[0][1].put("0", 1); mapMultiArray[1][0] = new LinkedHashMap<>(1); mapMultiArray[1][0].put("1", 0); mapMultiArray[1][1] = new LinkedHashMap<>(1); mapMultiArray[1][1].put("1", 1); assertEquals("[[{\"0\":0},{\"0\":1}],[{\"1\":0},{\"1\":1}]]", nullableJsonb.toJson(mapMultiArray)); } @Test public void testMarshallEnumSet() { final EnumSet languageEnumSet = EnumSet.of(Language.Czech, Language.Slovak); final String result = nullableJsonb.toJson(languageEnumSet); assertTrue("[\"Czech\",\"Slovak\"]".equals(result) || "[\"Slovak\",\"Czech\"]".equals(result)); assertEquals(languageEnumSet, nullableJsonb.fromJson(result, new TestTypeToken>() {}.getType())); } @Test public void testMarshallEnumMap() { final EnumMap languageEnumMap = new EnumMap<>(Language.class); languageEnumMap.put(Language.Russian, "ru"); languageEnumMap.put(Language.English, "en"); final String result = nullableJsonb.toJson(languageEnumMap); assertTrue("{\"Russian\":\"ru\",\"English\":\"en\"}".equals(result) || "{\"English\":\"en\",\"Russian\":\"ru\"}".equals(result)); } @Test @SuppressWarnings("unchecked") public void testRawCollection() { List rawList = new ArrayList(); rawList.add("first"); Circle circle = new Circle(); circle.setRadius(2.0); circle.setArea(1.0); rawList.add(circle); String expected = "[\"first\",{\"area\":1.0,\"radius\":2.0}]"; assertEquals(expected, nullableJsonb.toJson(rawList)); List result = nullableJsonb.fromJson(expected, List.class); assertEquals("first", result.get(0)); assertEquals(new BigDecimal("2.0"), ((Map)result.get(1)).get("radius")); assertEquals(new BigDecimal("1.0"), ((Map)result.get(1)).get("area")); } @Test public void testNavigableMap() { NavigableMap map = new TreeMap<>(); map.put("first", "abc"); map.put("second", "def"); final String json = nullableJsonb.toJson(map); assertEquals("{\"first\":\"abc\",\"second\":\"def\"}", json); NavigableMap result = nullableJsonb.fromJson(json, new TestTypeToken>() {}.getType()); assertEquals(TreeMap.class, result.getClass()); assertEquals("abc", result.get("first")); assertEquals("def", result.get("second")); } @Test public void testSortedMap() { SortedMap map = new TreeMap<>(); map.put("first", "abc"); map.put("second", "def"); final String json = nullableJsonb.toJson(map); assertEquals("{\"first\":\"abc\",\"second\":\"def\"}", json); SortedMap result = nullableJsonb.fromJson(json, new TestTypeToken>() {}.getType()); assertEquals(TreeMap.class, result.getClass()); assertEquals("abc", result.get("first")); assertEquals("def", result.get("second")); } public static class ConcurrentMapContainer { public ConcurrentMap concurrentMap; public ConcurrentHashMap concurrentHashMap; public ConcurrentNavigableMap concurrentNavigableMap; public ConcurrentSkipListMap concurrentSkipListMap; } @Test public void testConcurrentMaps() { // ConcurrentMap ConcurrentMapContainer c = new ConcurrentMapContainer(); c.concurrentMap = new ConcurrentHashMap(); c.concurrentMap.put("foo", "fooVal"); c.concurrentMap.put("bar", "barVal"); String expectedJson = "{\"concurrentMap\":{\"bar\":\"barVal\",\"foo\":\"fooVal\"}}"; assertEquals(expectedJson, defaultJsonb.toJson(c)); assertEquals(c.concurrentMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentMap); // ConcurrentHashMap c = new ConcurrentMapContainer(); c.concurrentHashMap = new ConcurrentHashMap(); c.concurrentHashMap.put("foo", "fooVal2"); c.concurrentHashMap.put("bar", "barVal2"); expectedJson = "{\"concurrentHashMap\":{\"bar\":\"barVal2\",\"foo\":\"fooVal2\"}}"; assertEquals(expectedJson, defaultJsonb.toJson(c)); assertEquals(c.concurrentHashMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentHashMap); // ConcurrentNavigableMap c = new ConcurrentMapContainer(); c.concurrentNavigableMap = new ConcurrentSkipListMap(); c.concurrentNavigableMap.put("foo", "fooVal3"); c.concurrentNavigableMap.put("bar", "barVal3"); expectedJson = "{\"concurrentNavigableMap\":{\"bar\":\"barVal3\",\"foo\":\"fooVal3\"}}"; assertEquals(expectedJson, defaultJsonb.toJson(c)); assertEquals(c.concurrentNavigableMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentNavigableMap); // ConcurrentSkipListMap c = new ConcurrentMapContainer(); c.concurrentSkipListMap = new ConcurrentSkipListMap(); c.concurrentSkipListMap.put("foo", "fooVal4"); c.concurrentSkipListMap.put("bar", "barVal4"); expectedJson = "{\"concurrentSkipListMap\":{\"bar\":\"barVal4\",\"foo\":\"fooVal4\"}}"; assertEquals(expectedJson, defaultJsonb.toJson(c)); assertEquals(c.concurrentSkipListMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentSkipListMap); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/collections/Language.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.collections; /** * Test enumeration. * * @author Dmitry Kornilov */ public enum Language { English, Russian, Slovak, Czech } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/collections/MapKeyTypesTest.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.collections; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.UUID; import org.eclipse.yasson.Jsonbs; import org.eclipse.yasson.TestTypeToken; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests to verify proper map key serialization and deserialization. */ public class MapKeyTypesTest { @Test public void uuidMapKey() { String expected = "{\"ccf3e2d3-3589-4c91-9ba9-1250ef515327\":{\"firstName\":\"FirstName1\",\"sureName\":\"SureName1\"}," + "\"0bb54cee-d538-428b-9719-5cc38c988522\":{\"firstName\":\"FirstName2\",\"sureName\":\"SureName2\"}}"; Map map = new HashMap<>(); Person person = new Person(); person.firstName = "FirstName1"; person.sureName = "SureName1"; Person person2 = new Person(); person2.firstName = "FirstName2"; person2.sureName = "SureName2"; map.put(UUID.fromString("ccf3e2d3-3589-4c91-9ba9-1250ef515327"), person); map.put(UUID.fromString("0bb54cee-d538-428b-9719-5cc38c988522"), person2); assertEquals(expected, Jsonbs.defaultJsonb.toJson(map)); assertEquals(map, Jsonbs.defaultJsonb.fromJson(expected, new TestTypeToken>() { }.getType())); } @Test public void zonedDateTimeMapKey() { ZonedDateTime zonedDateTime = ZonedDateTime.of(2020, 9, 14, 9, 33, 12, 0, ZoneId.of("UTC")); ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2019, 8, 13, 8, 32, 11, 1234, ZoneId.of("UTC")); String expected = "{\"2020-09-14T09:33:12Z[UTC]\":{\"firstName\":\"FirstName1\"," + "\"sureName\":\"SureName1\"}," + "\"2019-08-13T08:32:11.000001234Z[UTC]\":{\"firstName\":\"FirstName2\"," + "\"sureName\":\"SureName2\"}}"; Map map = new HashMap<>(); Person person = new Person(); person.firstName = "FirstName1"; person.sureName = "SureName1"; Person person2 = new Person(); person2.firstName = "FirstName2"; person2.sureName = "SureName2"; map.put(zonedDateTime, person); map.put(zonedDateTime2, person2); assertEquals(expected, Jsonbs.defaultJsonb.toJson(map)); assertEquals(map, Jsonbs.defaultJsonb.fromJson(expected, new TestTypeToken>() { }.getType())); } public static final class Person { public String firstName; public String sureName; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Person person = (Person) o; return Objects.equals(firstName, person.firstName) && Objects.equals(sureName, person.sureName); } @Override public int hashCode() { return Objects.hash(firstName, sureName); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; import java.time.MonthDay; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.SimpleTimeZone; import java.util.TimeZone; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.config.PropertyVisibilityStrategy; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.defaultmapping.dates.model.CalendarPojo; import org.eclipse.yasson.defaultmapping.dates.model.ClassLevelDateAnnotation; import org.eclipse.yasson.defaultmapping.dates.model.DatePojo; import org.eclipse.yasson.defaultmapping.dates.model.DateWithZonePojo; import org.eclipse.yasson.defaultmapping.dates.model.InstantPojo; import org.eclipse.yasson.defaultmapping.dates.model.LocalDatePojo; import org.eclipse.yasson.defaultmapping.dates.model.LocalDateTimePojo; import org.eclipse.yasson.defaultmapping.dates.model.LocalTimePojo; import org.eclipse.yasson.defaultmapping.dates.model.MonthDayPojo; import org.eclipse.yasson.defaultmapping.dates.model.OffsetDateTimePojo; import org.eclipse.yasson.defaultmapping.dates.model.OffsetTimePojo; import org.eclipse.yasson.defaultmapping.dates.model.YearMonthPojo; import org.eclipse.yasson.defaultmapping.dates.model.ZonedDateTimePojo; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import org.eclipse.yasson.internal.deserializer.types.SqlDateDeserializer; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.bindingJsonb; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; /** * This class contains tests for marshalling/unmarshalling dates. * * @author Dmitry Kornilov */ public class DatesTest { private static LocalDate localDate = LocalDate.of(2018, 1, 31); @SuppressWarnings("serial") public static class LocalDateObj implements Serializable { public LocalDate date = localDate; } @SuppressWarnings("serial") public static class SqlDateObj implements Serializable { public java.sql.Date sqlDate = java.sql.Date.valueOf("2018-01-31"); //no way for runtime to choose java.sql.Date deserializer here without a hint @JsonbTypeDeserializer(SqlDateDeserializer.class) public java.util.Date utilDate = java.sql.Date.valueOf("2018-01-31"); } public static class SqlDateFormatted { @JsonbDateFormat(value = "yyyy-MM-dd") public java.sql.Date sqlDate; @JsonbDateFormat(value = "yyyy-MM-dd") public java.util.Date utilDate; } @Test public void testSqlTimestamp() { Timestamp expectedTimestamp = Timestamp.from(Instant.now()); String json = bindingJsonb.toJson(expectedTimestamp); Timestamp timestamp = bindingJsonb.fromJson(json, Timestamp.class); assertEquals(expectedTimestamp, timestamp); } @Test public void testMarshallSqlDate() { String jsonString = bindingJsonb.toJson(new SqlDateObj()); assertEquals("{\"sqlDate\":\"2018-01-31Z\",\"utilDate\":\"2018-01-31Z\"}", jsonString); } @Test public void testUnmarshallSqlDate() { SqlDateObj result = bindingJsonb.fromJson("{\"sqlDate\":\"2018-01-31Z\",\"utilDate\":\"2018-01-31Z\"}", SqlDateObj.class); assertEquals("2018-01-31", result.sqlDate.toString()); assertEquals("2018-01-31", result.utilDate.toString()); } @Test public void testMarshallSqlDateFormatted() { final String date = "2026-02-25"; final String expectedJson = String.format("{\"sqlDate\":\"%1$s\",\"utilDate\":\"%1$s\"}", date); final SqlDateFormatted sqlDateFormatted = new SqlDateFormatted(); sqlDateFormatted.sqlDate = java.sql.Date.valueOf(date); // We use a java.sql.Date here as we want to test as if this was a Jakarta Persistence temporal date sqlDateFormatted.utilDate = java.sql.Date.valueOf(date); String jsonString = bindingJsonb.toJson(sqlDateFormatted); assertEquals(expectedJson, jsonString); // Unmarshal the object final SqlDateFormatted result = bindingJsonb.fromJson(jsonString, SqlDateFormatted.class); assertEquals(sqlDateFormatted.sqlDate, result.sqlDate); // The Date objects will not be equal unless user.timezone is set to UTC. The sqlDateFormatted.utilDate is // created at midnight in the current timezone (via valueOf()), while result.utilDate is created at midnight UTC // per the JSON-B specification. To verify both represent the same calendar date, we convert each to LocalDate // using its respective timezone: the original uses systemDefault(), the deserialized uses UTC. assertEquals(Instant.ofEpochMilli(sqlDateFormatted.utilDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDate(), result.utilDate.toInstant().atZone(ZoneOffset.UTC).toLocalDate()); } @Test public void testUnmarshallSqlDateFormatted() { final String date = "2026-02-25"; final String expectedString = String.format("{\"sqlDate\":\"%1$s\",\"utilDate\":\"%1$s\"}", date); final SqlDateFormatted sqlDateFormatted = bindingJsonb.fromJson(expectedString, SqlDateFormatted.class); assertEquals(date, sqlDateFormatted.sqlDate.toString()); // Convert java.util.Date to LocalDate for comparison final LocalDate resultDate = sqlDateFormatted.utilDate.toInstant() .atZone(ZoneOffset.UTC) .toLocalDate(); assertEquals(LocalDate.parse(date), resultDate); // Unmarshal the object final String result = bindingJsonb.toJson(sqlDateFormatted); assertEquals(expectedString, result); } public static class YearMonthFormatted { @JsonbDateFormat(value = "yyyy-MM") public java.util.Date date; } @Test public void testMarshallYearMonthFormat() { final YearMonthFormatted yearMonthFormatted = new YearMonthFormatted(); yearMonthFormatted.date = java.sql.Date.valueOf("2026-02-25"); String jsonString = bindingJsonb.toJson(yearMonthFormatted); assertEquals("{\"date\":\"2026-02\"}", jsonString); } @Test public void testUnmarshallYearMonthFormat() { final YearMonthFormatted yearMonthFormatted = bindingJsonb.fromJson( "{\"date\":\"2026-02\"}", YearMonthFormatted.class); final LocalDate resultDate = yearMonthFormatted.date.toInstant() .atZone(ZoneOffset.UTC) .toLocalDate(); assertEquals(LocalDate.of(2026, 2, 1), resultDate); } @Test public void testDateOnlyPatternEdgeCases() { // Test various edge cases to ensure date values are preserved correctly testDateRoundTrip("2028-03-01"); // Day after leap year testDateRoundTrip("2026-12-31"); // Last day of year testDateRoundTrip("2028-02-29"); // Leap year day testDateRoundTrip("2027-01-01"); // First day of year testDateRoundTrip("2028-01-31"); // Last day of January testDateRoundTrip("2028-02-01"); // First day of February } private void testDateRoundTrip(final String date) { final String json = String.format("{\"sqlDate\":\"%1$s\",\"utilDate\":\"%1$s\"}", date); // Deserialize final SqlDateFormatted deserialized = bindingJsonb.fromJson(json, SqlDateFormatted.class); // Verify utilDate represents midnight UTC for the specified date final LocalDate resultDate = deserialized.utilDate.toInstant() .atZone(ZoneOffset.UTC) .toLocalDate(); assertEquals(LocalDate.parse(date), resultDate, () -> String.format("Date should be %s when viewed in UTC", date)); // Verify JSON round-trip final String roundTripped = bindingJsonb.toJson(deserialized); assertEquals(json, roundTripped, () -> String.format("JSON should round-trip correctly for %s", date)); } @Test public void testSqlDateTimeZonesFormatted() { testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia"))); testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("US/Hawaii"))); testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney"))); } private void testSqlDateWithTZFormatted(TimeZone tz) { final TimeZone originalTZ = TimeZone.getDefault(); TimeZone.setDefault(tz); try { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault())); java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); String json = jsonb.toJson(d); assertEquals("\"1966-11-04\"", json); assertEquals("1966-11-04", jsonb.fromJson(json, java.sql.Date.class).toString()); } finally { TimeZone.setDefault(originalTZ); } } @Test public void testSqlDateTimeZonesMillis() { testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia")), -99712800000L); testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("US/Hawaii")), -99669600000L); testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney")), -99741600000L); } private void testSqlDateTimeZonesMillis(TimeZone tz, long expectedMs) { final TimeZone originalTZ = TimeZone.getDefault(); TimeZone.setDefault(tz); JsonbConfig jsonbConfig = new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault()); try (Jsonb jsonb = JsonbBuilder.create(jsonbConfig)) { java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); String json = jsonb.toJson(d); assertEquals("" + expectedMs, json); assertEquals(d, jsonb.fromJson(json, java.sql.Date.class)); } catch (Exception e) { throw new RuntimeException(e); } finally { TimeZone.setDefault(originalTZ); } } @Test public void testSqlDateTimeZones() { testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia"))); testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("US/Hawaii"))); testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney"))); } private void testSqlDateWithTZ(TimeZone tz) { final TimeZone originalTZ = TimeZone.getDefault(); TimeZone.setDefault(tz); try { java.sql.Date d = java.sql.Date.valueOf("1966-11-04"); String json = defaultJsonb.toJson(d); assertEquals("\"1966-11-04Z\"", json); assertEquals("1966-11-04", defaultJsonb.fromJson(json, java.sql.Date.class).toString()); } finally { TimeZone.setDefault(originalTZ); } } @Test public void testMarshallLocalDate() { String jsonString = bindingJsonb.toJson(new LocalDateObj()); if (jsonString.contains("T")) { fail("JSON contains time for a non Date that doesn't include time"); } } @Test public void testUnmarshallLocalDate() { LocalDateObj localDateObj = bindingJsonb.fromJson("{\"date\":\"2018-01-31\"}", LocalDateObj.class); assertEquals(localDate, localDateObj.date); } @Test public void testDate() throws ParseException { final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); final Date parsedDate = sdf.parse("04.03.2015"); final DatePojo pojo = new DatePojo(); pojo.customDate = parsedDate; pojo.defaultFormatted = parsedDate; pojo.millisFormatted = parsedDate; // marshal to ISO format final String expected = "{\"defaultFormatted\":\"2015-03-04T00:00:00Z[UTC]\"," + "\"millisFormatted\":" + parsedDate.getTime()+ "," + "\"customDate\":\"00:00:00 | 04-03-2015\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final DatePojo result = bindingJsonb.fromJson(expected, DatePojo.class); assertEquals(parsedDate, result.customDate); assertEquals(parsedDate, result.defaultFormatted); assertEquals(parsedDate, result.millisFormatted); } @Test public void testDateWithZoneOffset() throws ParseException { // Test for Yasson-172 final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); final Date parsedDate = sdf.parse("2018-11-02T00:00:00+01:00"); String jsonDateWithZone = "{\"dateWithZone\":\"2018-11-02T00:00:00+01:00\"}"; final DateWithZonePojo result = bindingJsonb.fromJson(jsonDateWithZone, DateWithZonePojo.class); assertEquals(parsedDate, result.dateWithZone); } @Test public void testDateWithZoneId() throws ParseException { // Test for Yasson-172 final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); final Date parsedDate = sdf.parse("2018-11-02T00:00:00+01:00"); String jsonDateWithZone = "{\"dateWithZone\":\"2018-11-02T00:00:00+01:00[Europe/Berlin]\"}"; final DateWithZonePojo result = bindingJsonb.fromJson(jsonDateWithZone, DateWithZonePojo.class); assertEquals(parsedDate, result.dateWithZone); } @Test public void testCalendar() { final Calendar timeCalendar = new Calendar.Builder() .setDate(2015, Calendar.APRIL, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("Europe/Prague")) .build(); final CalendarPojo calendarPojo = new CalendarPojo(); calendarPojo.customCalendar = timeCalendar; calendarPojo.defaultFormatted = timeCalendar; calendarPojo.millisFormatted = timeCalendar; // marshal to ISO_DATE final String expected = "{\"defaultFormatted\":\"2015-04-03T11:11:10+02:00[Europe/Prague]\"," + "\"millisFormatted\":1428052270000," + "\"customCalendar\":\"11:11:10 | 03-04-2015, +0200\"}"; assertEquals(expected, bindingJsonb.toJson(calendarPojo)); // marshal to ISO_DATE_TIME final CalendarPojo result = bindingJsonb.fromJson(expected, CalendarPojo.class); assertEquals(timeCalendar.getTime(), result.customCalendar.getTime()); assertEquals(timeCalendar.getTime(), result.millisFormatted.getTime()); assertEquals(timeCalendar.getTime(), result.defaultFormatted.getTime()); } @Test public void testCalendarWithoutTime() { ScalarValueWrapper result = bindingJsonb.fromJson("{\"value\":\"2015-04-03+01:00\"}", new TestTypeToken>(){}.getType()); assertEquals(2015, result.getValue().get(Calendar.YEAR)); assertEquals(3, result.getValue().get(Calendar.MONTH)); assertEquals(3, result.getValue().get(Calendar.DAY_OF_MONTH)); assertEquals(0, result.getValue().get(Calendar.HOUR_OF_DAY)); assertEquals(0, result.getValue().get(Calendar.MINUTE)); assertEquals(0, result.getValue().get(Calendar.SECOND)); assertEquals("GMT+01:00", result.getValue().getTimeZone().toZoneId().toString()); } @Test public void testCalendarWithNonDefaultTimeZone() { final ZoneId zoneId = ZoneId.of("Europe/Prague"); final Calendar cal = new Calendar.Builder() .setDate(2015, Calendar.APRIL, 3) .setTimeOfDay(10, 10, 10) .setTimeZone(TimeZone.getTimeZone(zoneId)) .build(); final CalendarPojo calendarPojo = new CalendarPojo(); calendarPojo.customCalendar = cal; calendarPojo.defaultFormatted = cal; calendarPojo.millisFormatted = cal; // marshal to ISO_DATE final String expected = "{\"defaultFormatted\":\"2015-04-03T10:10:10+02:00[" + zoneId + "]\"," + "\"millisFormatted\":" + cal.getTimeInMillis() + "," + "\"customCalendar\":\"10:10:10 | 03-04-2015, +0200\"}"; assertEquals(expected, bindingJsonb.toJson(calendarPojo)); // marshal to ISO_DATE_TIME final CalendarPojo result = bindingJsonb.fromJson(expected, CalendarPojo.class); assertEquals(cal.getTime(), result.customCalendar.getTime()); assertEquals(cal.getTime(), result.millisFormatted.getTime()); assertEquals(cal.getTime(), result.defaultFormatted.getTime()); } @Test public void testMarshalGregorianCalendar() { final Calendar cal = GregorianCalendar.getInstance(); cal.clear(); cal.set(2015, Calendar.APRIL, 3); cal.setTimeZone(TimeZone.getTimeZone("UTC")); // marshal to ISO_DATE assertEquals("{\"value\":\"2015-04-03Z\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(cal))); // marshal to ISO_DATE_TIME final Calendar dateTimeGregorianCalendar = new Calendar.Builder().setDate(2015, 3, 3) .setTimeZone(TimeZone.getTimeZone("Europe/Prague")) .build(); assertEquals("{\"value\":\"2015-04-03T00:00:00+02:00[Europe/Prague]\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(dateTimeGregorianCalendar))); } @Test public void testMarshalTimeZone() { assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(TimeZone.getTimeZone("Europe/Prague")))); assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(SimpleTimeZone.getTimeZone("Europe/Prague")))); } @Test public void testMarshalInstant() { final Instant instant = Instant.parse("2015-03-03T23:00:00Z"); InstantPojo instantPojo = new InstantPojo(instant); final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," + "\"millisFormatted\":1425423600000," + "\"instant\":\"23:00:00 | 03-03-2015\"}"; assertEquals(expected, bindingJsonb.toJson(instantPojo)); InstantPojo result = bindingJsonb.fromJson(expected, InstantPojo.class); assertEquals(instant, result.defaultFormatted); assertEquals(instant, result.millisFormatted); assertEquals(instant, result.instant); } @Test public void testDateFormattedAsMillisInString() { Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withTimeInMillisAsAString(true)); final Instant instant = Instant.parse("2015-03-03T23:00:00Z"); InstantPojo instantPojo = new InstantPojo(instant); final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," + "\"millisFormatted\":\"1425423600000\"," + "\"instant\":\"23:00:00 | 03-03-2015\"}"; assertEquals(expected, jsonb.toJson(instantPojo)); InstantPojo result = jsonb.fromJson(expected, InstantPojo.class); assertEquals(instant, result.defaultFormatted); assertEquals(instant, result.millisFormatted); assertEquals(instant, result.instant); } @Test public void testMarshalDuration() { assertEquals("{\"value\":\"PT5H4M\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Duration.ofHours(5).plusMinutes(4)))); } @Test public void testMarshalPeriod() { final Period period = Period.between(LocalDate.of(1960, Month.JANUARY, 1), LocalDate.of(1970, Month.JANUARY, 1)); final ScalarValueWrapper value = new ScalarValueWrapper<>(period); assertEquals("{\"value\":\"P10Y\"}", bindingJsonb.toJson(value)); } @Test public void testLocalDate() { final LocalDate localDate = LocalDate.of(2015, Month.APRIL, 10); final LocalDatePojo pojo = new LocalDatePojo(localDate); // Get proper milliseconds final long millis = localDate.atStartOfDay(ZoneId.of("Z")).toInstant().toEpochMilli(); final String expected = "{\"defaultFormatted\":\"2015-04-10\"," + "\"millisFormatted\":" + millis + "," + "\"customLocalDate\":\"10-04-2015\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final LocalDatePojo result = bindingJsonb.fromJson(expected, LocalDatePojo.class); assertEquals(localDate, result.customLocalDate); assertEquals(localDate, result.millisFormatted); assertEquals(localDate, result.defaultFormatted); } @Test public void testlLocalTime() { final Jsonb jsonb = getJsonbWithMillisIgnored(); final LocalTime localTime = LocalTime.of(22, 33); final LocalTimePojo localTimePojo = new LocalTimePojo(localTime); final String expected = "{\"defaultFormatted\":\"22:33:00\",\"localTime\":\"22:33:00\"}"; assertEquals(expected, jsonb.toJson(localTimePojo)); final LocalTimePojo result = jsonb.fromJson(expected, LocalTimePojo.class); assertEquals(localTime, result.defaultFormatted); assertEquals(localTime, result.localTime); } private Jsonb getJsonbWithMillisIgnored() { final JsonbConfig config = new JsonbConfig(); config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { return !field.getName().startsWith("millis"); } @Override public boolean isVisible(Method method) { return false; } }); return JsonbBuilder.create(config); } @Test public void testLocalDateTime() { final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21); final LocalDateTimePojo pojo = new LocalDateTimePojo(dateTime); // Get proper milliseconds final long millis = dateTime.atZone(ZoneId.of("Z")).toInstant().toEpochMilli(); final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00\"," + "\"millisFormatted\":" + millis + "," + "\"customLocalDate\":\"16-02-2015--00:21:13\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final LocalDateTimePojo result = bindingJsonb.fromJson(expected, LocalDateTimePojo.class); assertEquals(dateTime, result.defaultFormatted); assertEquals(dateTime, result.millisFormatted); assertEquals(dateTime, result.customLocalDate); } @Test public void testLocalDateTimeWithoutConfig() { final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21); final ScalarValueWrapper pojo = new ScalarValueWrapper<>(); pojo.setValue(dateTime); final String expected = "{\"value\":\"2015-02-16T13:21:00\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final ScalarValueWrapper result = bindingJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(dateTime, result.getValue()); } @Test public void testDifferentConfigsLocalDateTime() { final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21); final long millis = dateTime.atZone(ZoneId.of("Z")).toInstant().toEpochMilli(); final ScalarValueWrapper pojo = new ScalarValueWrapper<>(); pojo.setValue(dateTime); final String expected = "{\"value\":\"2015-02-16T13:21:00\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final Jsonb jsonbCustom = JsonbBuilder.create(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.FRENCH)); assertEquals("{\"value\":" + millis + "}", jsonbCustom.toJson(pojo)); ScalarValueWrapper result = bindingJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(dateTime, result.getValue()); result = jsonbCustom.fromJson("{\"value\":\"" + millis + "\"}", new TestTypeToken>(){}.getType()); assertEquals(dateTime, result.getValue()); } @Test public void testZonedDateTime() { final ZoneId zone = ZoneId.of("Asia/Almaty"); final ZonedDateTime dateTime = ZonedDateTime.of(2015, 2, 16, 13, 21, 0, 0, zone); final ZonedDateTimePojo pojo = new ZonedDateTimePojo(dateTime); final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+06:00[" + zone + "]\"," + "\"millisFormatted\":" + dateTime.toInstant().toEpochMilli() + "," + "\"customZonedDate\":\"+06" + zone + " | 16-02-2015--00:21:13\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final ZonedDateTimePojo result = bindingJsonb.fromJson(expected, ZonedDateTimePojo.class); assertEquals(dateTime, result.defaultFormatted); assertEquals(dateTime, result.customZonedDate); // time zone and seconds omitted final ZonedDateTimePojo result1 = bindingJsonb.fromJson("{\"defaultFormatted\":\"2015-02-16T13:21+06:00\"}", ZonedDateTimePojo.class); assertEquals(dateTime.getHour(), result1.defaultFormatted.getHour()); assertEquals(dateTime.getOffset(), result1.defaultFormatted.getOffset()); } @Test public void testMarshalZoneId() { assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(ZoneId.of("Europe/Prague")))); } @Test public void testMarshalZoneOffset() { assertEquals("{\"value\":\"+02:00\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(ZoneOffset.of("+02:00")))); } @Test public void testMarshalOffsetDateTime() { final OffsetDateTime dateTime = OffsetDateTime.of(2015, 2, 16, 13, 21, 0, 0, ZoneOffset.of("+05:00")); final OffsetDateTimePojo pojo = new OffsetDateTimePojo(dateTime); final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+05:00\"," + "\"millisFormatted\":1424074860000," + "\"offsetDateTime\":\"+0500 16-02-2015--00:21:13\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final OffsetDateTimePojo result = bindingJsonb.fromJson(expected, OffsetDateTimePojo.class); assertEquals(dateTime, result.defaultFormatted); assertEquals(dateTime, result.offsetDateTime); } @Test public void testMarshalOffsetTime() { final Jsonb jsonb = getJsonbWithMillisIgnored(); final OffsetTime dateTime = OffsetTime.of(13, 21, 15, 0, ZoneOffset.of("+05:00")); final OffsetTimePojo pojo = new OffsetTimePojo(dateTime); final String expected = "{\"defaultFormatted\":\"13:21:15+05:00\",\"offsetTime\":\"13:21:15+0500\"}"; assertEquals(expected, jsonb.toJson(pojo)); final OffsetTimePojo result = jsonb.fromJson(expected, OffsetTimePojo.class); assertEquals(dateTime, result.defaultFormatted); assertEquals(dateTime, result.offsetTime); } @Test public void testClassLevel() throws ParseException { final ClassLevelDateAnnotation pojo = new ClassLevelDateAnnotation(); final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); pojo.date = sdf.parse("04.03.2015"); final ZoneId zone = ZoneId.of("Asia/Almaty"); pojo.calendar = new Calendar.Builder().setDate(2015, Calendar.APRIL, 3).setTimeOfDay(11, 11, 10).setTimeZone(TimeZone.getTimeZone(zone)).build(); pojo.zonedDateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, zone); pojo.defaultZoned = pojo.zonedDateTime; pojo.localDateTime = LocalDateTime.of(2015, 4, 3, 13, 21, 0, 0); final String expected = "{\"date\":\"04-03-2015 00:00:00\"," + "\"localDateTime\":\"03-04-2015 13:21:00\"," + "\"calendar\":\"+06 ALMT ven. avril 03-04-2015 11:11:10\"," + "\"defaultZoned\":\"2015-04-03T13:21:00+06:00[Asia/Almaty]\"," + "\"zonedDateTime\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}"; assertEquals(expected, bindingJsonb.toJson(pojo)); final ClassLevelDateAnnotation result = bindingJsonb.fromJson(expected, ClassLevelDateAnnotation.class); assertEquals(pojo.date, result.date); assertEquals(pojo.localDateTime, result.localDateTime); assertEquals(pojo.calendar.getTime(), result.calendar.getTime()); assertEquals(pojo.zonedDateTime, result.zonedDateTime); } @Test public void testGlobalConfigDateFormat() { final JsonbConfig config = new JsonbConfig(); config.withDateFormat("X z E MMMM dd-MM-yyyy HH:mm:ss", Locale.FRENCH); final Jsonb jsonb = JsonbBuilder.create(config); final ZonedDateTime dateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, ZoneId.of("Asia/Almaty")); final String expected = "{\"value\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}"; assertEquals(expected, jsonb.toJson(new ScalarValueWrapper<>(dateTime))); final ScalarValueWrapper result = jsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(dateTime, result.getValue()); } @Test public void testDateFrenchLocale() { String format = "E DD MMM yyyy HH:mm:ss z"; Locale locale = Locale.forLanguageTag("fr-FR"); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat(format, locale)); final ScalarValueWrapper result = jsonb.fromJson("{ \"value\" : \"lun. 93 avr. 2017 16:51:12 CEST\" }", new TestTypeToken>(){}.getType()); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); final Instant instant = Instant.from(formatter.withLocale(locale).parse("lun. 93 avr. 2017 16:51:12 CEST")); assertEquals(instant, result.getValue().toInstant()); } @Test public void testSimpleTimeZone() { String json = bindingJsonb.toJson(new ScalarValueWrapper<>(new SimpleTimeZone(0, "America/Los_Angeles"))); assertEquals("{\"value\":\"America/Los_Angeles\"}", json); ScalarValueWrapper result = bindingJsonb.fromJson(json, new TestTypeToken>() { }.getType()); assertEquals("America/Los_Angeles", result.getValue().getID()); assertEquals(LocalDateTime.now().atZone(ZoneId.of("America/Los_Angeles")).getOffset().getTotalSeconds() * 1000, result.getValue().getOffset(System.currentTimeMillis())); } @Test public void testDateInMap() { JsonbConfig config = new JsonbConfig() .withDateFormat("yyyy", Locale.ENGLISH); Jsonb jsonb = JsonbBuilder.create(config); LocalDate localDate = LocalDate.of(2017, 9, 14); DateInMapPojo pojo = new DateInMapPojo(); pojo.setLocalDate(localDate); pojo.setDateMap(new HashMap<>()); pojo.getDateMap().put("first", localDate); String json = jsonb.toJson(pojo); assertEquals("{\"dateMap\":{\"first\":\"2017\"},\"localDate\":\"2017\"}", json); config = new JsonbConfig() .withDateFormat("dd.MM.yyyy", Locale.ENGLISH); jsonb = JsonbBuilder.create(config); DateInMapPojo result = jsonb.fromJson("{\"dateMap\":{\"first\":\"01.01.2017\"},\"localDate\":\"01.01.2017\"}", DateInMapPojo.class); assertEquals(LocalDate.of(2017,1,1), result.localDate); assertEquals(LocalDate.of(2017,1,1), result.dateMap.get("first")); } @Test public void testYearMonth() { YearMonthPojo yearMonthPojo = new YearMonthPojo(); yearMonthPojo.yearMonth = YearMonth.of(2019, Month.MAY); yearMonthPojo.yearMonthWithFormatter = YearMonth.of(2019, Month.MAY); String expected = "{\"yearMonth\":\"2019-05\",\"yearMonthWithFormatter\":\"05-2019\"}"; String serialized = bindingJsonb.toJson(yearMonthPojo); assertEquals(expected, serialized); YearMonthPojo deserialized = bindingJsonb.fromJson(expected, YearMonthPojo.class); assertEquals(yearMonthPojo, deserialized); } @Test public void testMonthDay() { MonthDayPojo monthDay = new MonthDayPojo(); monthDay.monthDay = MonthDay.of(Month.MAY, 2); monthDay.monthDayWithFormatter = MonthDay.of(Month.MAY, 2); String expected = "{\"monthDay\":\"--05-02\",\"monthDayWithFormatter\":\"02-05\"}"; String serialized = bindingJsonb.toJson(monthDay); assertEquals(expected, serialized); MonthDayPojo deserialized = bindingJsonb.fromJson(expected, MonthDayPojo.class); assertEquals(monthDay, deserialized); } @Test public void testXMLGregorianCalendar() throws DatatypeConfigurationException { final Calendar calendar = new Calendar.Builder() .setDate(2015, Calendar.APRIL, 3) .setTimeOfDay(11, 11, 10) .setTimeZone(TimeZone.getTimeZone("Europe/Prague")) .build(); final XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance() .newXMLGregorianCalendar((GregorianCalendar) calendar); String serialized = bindingJsonb.toJson(xmlGregorianCalendar); assertEquals("\"2015-04-03T11:11:10+02:00[GMT+02:00]\"", serialized); assertEquals(xmlGregorianCalendar, bindingJsonb.fromJson(serialized, XMLGregorianCalendar.class)); } public static class DateInMapPojo { private LocalDate localDate; private Map dateMap; public LocalDate getLocalDate() { return localDate; } public void setLocalDate(LocalDate localDate) { this.localDate = localDate; } public Map getDateMap() { return dateMap; } public void setDateMap(Map dateMap) { this.dateMap = dateMap; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/AbstractDateTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; /** * Common parent class for testing date objects. * Contains {@link JsonbDateFormat} DEFAULT_FORMAT and TIME_IN_MILLIS annotated date objects. * * @author Roman Grigoriadi */ public class AbstractDateTimePojo { public AbstractDateTimePojo() { } public AbstractDateTimePojo(T dateObj) { this.defaultFormatted = dateObj; this.millisFormatted = dateObj; } @JsonbDateFormat(JsonbDateFormat.DEFAULT_FORMAT) public T defaultFormatted; @JsonbDateFormat(JsonbDateFormat.TIME_IN_MILLIS) public T millisFormatted; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CalendarPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.util.Calendar; /** * @author Roman Grigoriadi */ public class CalendarPojo extends AbstractDateTimePojo { public CalendarPojo() { } public CalendarPojo(Calendar dateObj) { super(dateObj); this.customCalendar = dateObj; } @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy, Z") public Calendar customCalendar; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotation.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.ZonedDateTime; import java.util.Calendar; /** * @author Roman Grigoriadi */ @JsonbDateFormat(value = "X z E MMMM dd-MM-yyyy HH:mm:ss", locale = "fr") public class ClassLevelDateAnnotation extends ClassLevelDateAnnotationParent { public ZonedDateTime zonedDateTime; public Calendar calendar; @JsonbDateFormat(value = JsonbDateFormat.DEFAULT_FORMAT) public ZonedDateTime defaultZoned; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotationParent.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.LocalDateTime; import java.util.Date; /** * @author Roman Grigoriadi */ @JsonbDateFormat("dd-MM-yyyy HH:mm:ss") public class ClassLevelDateAnnotationParent { public Date date; public LocalDateTime localDateTime; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CollectionDatePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.util.Date; import java.util.List; /** * @author Roman Grigoriadi */ public class CollectionDatePojo { @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy") public List dates; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DatePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.util.Date; /** * @author Roman Grigoriadi */ public class DatePojo extends AbstractDateTimePojo { public DatePojo() { } public DatePojo(Date dateObj) { super(dateObj); this.customDate = dateObj; } @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy") public Date customDate; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DateWithZonePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import java.util.Date; /** * @author Dennis Kriechel */ public class DateWithZonePojo extends AbstractDateTimePojo { public DateWithZonePojo() { } public DateWithZonePojo(Date dateObj) { super(dateObj); this.dateWithZone = dateObj; } public Date dateWithZone; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/InstantPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.Instant; /** * @author Roman Grigoriadi */ public class InstantPojo extends AbstractDateTimePojo { public InstantPojo() { } public InstantPojo(Instant dateObj) { super(dateObj); this.instant = dateObj; } @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy") public Instant instant; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDatePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.LocalDate; /** * @author Roman Grigoriadi */ public class LocalDatePojo extends AbstractDateTimePojo { public LocalDatePojo() { } public LocalDatePojo(LocalDate date) { super(date); this.customLocalDate = date; } @JsonbDateFormat("dd-MM-yyyy") public LocalDate customLocalDate; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDateTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.LocalDateTime; /** * @author Roman Grigoriadi */ public class LocalDateTimePojo extends AbstractDateTimePojo { public LocalDateTimePojo() { } public LocalDateTimePojo(LocalDateTime date) { super(date); this.customLocalDate = date; } @JsonbDateFormat("dd-MM-yyyy--ss:mm:HH") public LocalDateTime customLocalDate; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.LocalTime; /** * @author Roman Grigoriadi */ public class LocalTimePojo extends AbstractDateTimePojo { public LocalTimePojo() { } public LocalTimePojo(LocalTime date) { super(date); this.localTime = date; } @JsonbDateFormat("HH:mm:ss") public LocalTime localTime; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/MonthDayPojo.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import java.time.MonthDay; import java.util.Objects; import jakarta.json.bind.annotation.JsonbDateFormat; /** * Pojo object of the {@link MonthDay}. */ public class MonthDayPojo { public MonthDay monthDay; @JsonbDateFormat("dd-MM") public MonthDay monthDayWithFormatter; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MonthDayPojo that = (MonthDayPojo) o; return Objects.equals(monthDay, that.monthDay) && Objects .equals(monthDayWithFormatter, that.monthDayWithFormatter); } @Override public int hashCode() { return Objects.hash(monthDay, monthDayWithFormatter); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetDateTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.OffsetDateTime; /** * @author Roman Grigoriadi */ public class OffsetDateTimePojo extends AbstractDateTimePojo { public OffsetDateTimePojo() { } public OffsetDateTimePojo(OffsetDateTime date) { super(date); this.offsetDateTime = date; } @JsonbDateFormat("Z dd-MM-yyyy--ss:mm:HH") public OffsetDateTime offsetDateTime; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.OffsetTime; /** * @author Roman Grigoriadi */ public class OffsetTimePojo extends AbstractDateTimePojo { public OffsetTimePojo() { } public OffsetTimePojo(OffsetTime date) { super(date); this.offsetTime = date; } @JsonbDateFormat("HH:mm:ssZ") public OffsetTime offsetTime; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/YearMonthPojo.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import java.time.YearMonth; import java.util.Objects; import jakarta.json.bind.annotation.JsonbDateFormat; /** * Pojo object of the {@link YearMonth}. */ public class YearMonthPojo { public YearMonth yearMonth; @JsonbDateFormat("MM-yyyy") public YearMonth yearMonthWithFormatter; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } YearMonthPojo that = (YearMonthPojo) o; return Objects.equals(yearMonth, that.yearMonth) && Objects .equals(yearMonthWithFormatter, that.yearMonthWithFormatter); } @Override public int hashCode() { return Objects.hash(yearMonth, yearMonthWithFormatter); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ZonedDateTimePojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.dates.model; import jakarta.json.bind.annotation.JsonbDateFormat; import java.time.ZonedDateTime; /** * @author Roman Grigoriadi */ public class ZonedDateTimePojo extends AbstractDateTimePojo { public ZonedDateTimePojo() { } public ZonedDateTimePojo(ZonedDateTime date) { super(date); this.customZonedDate = date; } @JsonbDateFormat("XVV | dd-MM-yyyy--ss:mm:HH") public ZonedDateTime customZonedDate; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java ================================================ /* * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TimeZone; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.adapters.model.GenericBox; import org.eclipse.yasson.defaultmapping.generics.model.AnotherGenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.BoundedGenericClass; import org.eclipse.yasson.defaultmapping.generics.model.Circle; import org.eclipse.yasson.defaultmapping.generics.model.CollectionContainer; import org.eclipse.yasson.defaultmapping.generics.model.CollectionElement; import org.eclipse.yasson.defaultmapping.generics.model.CollectionWrapper; import org.eclipse.yasson.defaultmapping.generics.model.ColoredCircle; import org.eclipse.yasson.defaultmapping.generics.model.ConstructorContainer; import org.eclipse.yasson.defaultmapping.generics.model.CyclicSubClass; import org.eclipse.yasson.defaultmapping.generics.model.FinalGenericWrapper; import org.eclipse.yasson.defaultmapping.generics.model.FinalMember; import org.eclipse.yasson.defaultmapping.generics.model.GenericArrayClass; import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.GenericWithUnboundedWildcardClass; import org.eclipse.yasson.defaultmapping.generics.model.LowerBoundTypeVariableWithCollectionAttributeClass; import org.eclipse.yasson.defaultmapping.generics.model.MultiLevelExtendedGenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.MultipleBoundsContainer; import org.eclipse.yasson.defaultmapping.generics.model.MyCyclicGenericClass; import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass; import org.eclipse.yasson.defaultmapping.generics.model.Shape; import org.eclipse.yasson.defaultmapping.generics.model.StaticCreatorContainer; import org.eclipse.yasson.defaultmapping.generics.model.TreeContainer; import org.eclipse.yasson.defaultmapping.generics.model.TreeElement; import org.eclipse.yasson.defaultmapping.generics.model.WildCardClass; import org.eclipse.yasson.defaultmapping.generics.model.WildcardMultipleBoundsClass; import org.eclipse.yasson.serializers.model.Box; import org.eclipse.yasson.serializers.model.Crate; import org.junit.jupiter.api.Test; /** * This class contains JSONB default mapping generics tests. * * @author Dmitry Kornilov */ public class GenericsTest { @Test public void testGenericClass() { GenericTestClass genericClass = new GenericTestClass<>(); genericClass.field1 = "value1"; genericClass.field2 = 3; String expected = "{\"field1\":\"value1\",\"field2\":3}"; assertEquals(expected, defaultJsonb.toJson(genericClass)); Type type = new TestTypeToken>(){}.getType(); GenericTestClass result = defaultJsonb.fromJson(expected, type); assertEquals("value1", result.field1); assertEquals(Integer.valueOf(3), result.field2); } @Test public void testMultiLevelGenericClass() { GenericTestClass innerMostGenericClass = new GenericTestClass<>(); innerMostGenericClass.field1 = "innerMostValue3"; innerMostGenericClass.field2 = 3; AnotherGenericTestClass, Integer> another = new AnotherGenericTestClass<>(); another.field1 = innerMostGenericClass; another.field2 = 2; GenericTestClass, Integer>> nestedGenericField = new GenericTestClass<>(); nestedGenericField.field1 = "outerValue1"; nestedGenericField.field2 = another; String expected = "{\"field1\":\"outerValue1\",\"field2\":{\"field1\":{\"field1\":\"innerMostValue3\",\"field2\":3},\"field2\":2}}"; assertEquals(expected, defaultJsonb.toJson(nestedGenericField)); Type type = new TestTypeToken, Integer>>>(){}.getType(); GenericTestClass, Integer>> result = defaultJsonb.fromJson(expected, type); assertEquals("outerValue1", result.field1); assertEquals(Integer.valueOf(2), result.field2.field2); assertEquals("innerMostValue3", result.field2.field1.field1); assertEquals(Integer.valueOf(3), result.field2.field1.field2); } @Test public void testNestedGenericSelfClass() { GenericTestClass inner = new GenericTestClass<>(); inner.field1 = "innerValue1"; inner.field2 = 5; GenericTestClass> nestedGenericOnSelfClass = new GenericTestClass<>(); nestedGenericOnSelfClass.field1 = "outerValue1"; nestedGenericOnSelfClass.field2 = inner; String expected = "{\"field1\":\"outerValue1\",\"field2\":{\"field1\":\"innerValue1\",\"field2\":5}}"; assertEquals(expected, defaultJsonb.toJson(nestedGenericOnSelfClass)); Type type = new TestTypeToken>>(){}.getType(); GenericTestClass> result = defaultJsonb.fromJson(expected, type); assertEquals("outerValue1", result.field1); assertEquals("innerValue1", result.field2.field1); assertEquals(Integer.valueOf(5), result.field2.field2); } @Test public void testCyclicGenericClass() { final MyCyclicGenericClass myCyclicGenericClass = new MyCyclicGenericClass<>(); final CyclicSubClass cyclicSubClass = new CyclicSubClass(); cyclicSubClass.subField = "subFieldValue"; myCyclicGenericClass.field1 = cyclicSubClass; String expected = "{\"field1\":{\"subField\":\"subFieldValue\"}}"; assertEquals(expected, defaultJsonb.toJson(myCyclicGenericClass)); MyCyclicGenericClass result = defaultJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(CyclicSubClass.class, result.field1.getClass()); assertEquals("subFieldValue", result.field1.subField); } @Test public void testWildCards() { final WildCardClass integerWildCard = new WildCardClass<>(); integerWildCard.number = 10; String expected = "{\"number\":10}"; assertEquals(expected, defaultJsonb.toJson(integerWildCard)); WildCardClass result = defaultJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(Integer.valueOf(10), result.number); } @Test public void testGenericWithUnboundedWildcard() { //wildcardList is treated as List String expected = "{\"wildcardList\":[{\"k1\":\"v1\",\"k2\":\"v2\"}]}"; GenericWithUnboundedWildcardClass genericWithUnboundedWildcardClass = new GenericWithUnboundedWildcardClass(); List> list = new ArrayList<>(); genericWithUnboundedWildcardClass.wildcardList = list; Map stringMap = new HashMap<>(); stringMap.put("k1", "v1"); stringMap.put("k2", "v2"); list.add(stringMap); assertEquals(expected, defaultJsonb.toJson(genericWithUnboundedWildcardClass)); GenericWithUnboundedWildcardClass result = defaultJsonb.fromJson(expected, GenericWithUnboundedWildcardClass.class); assertTrue(result.wildcardList.get(0) instanceof Map); assertEquals("v1", ((Map) result.wildcardList.get(0)).get("k1")); assertEquals("v2", ((Map) result.wildcardList.get(0)).get("k2")); } @Test public void testWildCardMultipleBounds() { WildcardMultipleBoundsClass multipleBoundsClass = new WildcardMultipleBoundsClass<>(); multipleBoundsClass.wildcardField = BigDecimal.ONE; GenericTestClass genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = "genericTestClassField1"; genericTestClass.field2 = BigDecimal.TEN; multipleBoundsClass.genericTestClassPropagatedWildCard = genericTestClass; List extendsBigDecimalList = new ArrayList<>(); extendsBigDecimalList.add(new ExtendsBigDecimal("11")); multipleBoundsClass.propagatedWildcardList = extendsBigDecimalList; String expected = "{\"genericTestClassPropagatedWildCard\":{\"field1\":\"genericTestClassField1\",\"field2\":10},\"propagatedWildcardList\":[11],\"wildcardField\":1}"; assertEquals(expected, defaultJsonb.toJson(multipleBoundsClass, new WildcardMultipleBoundsClass(){}.getClass())); WildcardMultipleBoundsClass result = defaultJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(BigDecimal.ONE, result.wildcardField); assertEquals("genericTestClassField1", result.genericTestClassPropagatedWildCard.field1); assertEquals(BigDecimal.TEN, result.genericTestClassPropagatedWildCard.field2); assertEquals(new BigDecimal("11"), result.propagatedWildcardList.get(0)); } @Test public void testWithType() { List> expected = Arrays.asList(Optional.empty(), Optional.of("first"), Optional.of("second")); //String json = jsonb.toJson(expected, DefaultMappingGenericsTest.class.getField("listOfOptionalStringField").getGenericType()); String json = defaultJsonb.toJson(expected); assertEquals("[null,\"first\",\"second\"]", json); } @Test public void testPropagatedGenerics() { List integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); GenericTestClass, String> genericInList = new GenericTestClass<>(); genericInList.field1 = integerList; genericInList.field2 = "GenericsInListF2"; List, String>> listWithGenerics = new ArrayList<>(); listWithGenerics.add(genericInList); GenericTestClass genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = 1; genericTestClass.field2 = "first"; PropagatedGenericClass propagatedGenericClass = new PropagatedGenericClass<>(); propagatedGenericClass.genericTestClass = genericTestClass; propagatedGenericClass.genericList = listWithGenerics; String expected = "{\"genericList\":[{\"field1\":[1,2],\"field2\":\"GenericsInListF2\"}],\"genericTestClass\":{\"field1\":1,\"field2\":\"first\"}}"; assertEquals(expected, defaultJsonb.toJson(propagatedGenericClass, new TestTypeToken>(){}.getType())); PropagatedGenericClass result = defaultJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(GenericTestClass.class, result.genericList.get(0).getClass()); assertEquals(Integer.valueOf(1), result.genericList.get(0).field1.get(0)); assertEquals(Integer.valueOf(2), result.genericList.get(0).field1.get(1)); assertEquals("GenericsInListF2", result.genericList.get(0).field2); assertEquals(GenericTestClass.class, result.genericTestClass.getClass()); assertEquals(Integer.valueOf(1), result.genericTestClass.field1); assertEquals("first", result.genericTestClass.field2); } @Test public void testMarshallPropagatedGenericsRaw() { List integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); GenericBox> box = new GenericBox<>(); box.setX(integerList); box.setStrField("IntegerListBox"); GenericTestClass>, String> pojo = new GenericTestClass<>(); pojo.field1 = box; pojo.field2 = "GenericTestClass"; assertEquals("{\"field1\":{\"strField\":\"IntegerListBox\",\"x\":[1,2]},\"field2\":\"GenericTestClass\"}", defaultJsonb.toJson(pojo)); } @Test public void testFunctional() { FunctionalInterface myFunction = new FunctionalInterface() { private String value = "initValue"; @Override public String getValue() { return value; } public void setValue(String value) { this.value = value; } }; assertEquals("{\"value\":\"initValue\"}", defaultJsonb.toJson(myFunction)); } @Test public void testBoundedGenerics() { //bounded generics BoundedGenericClass, Circle> boundedGenericClass = new BoundedGenericClass<>(); List shapeList = new ArrayList<>(); Circle circle = new Circle(); circle.setRadius(2.5); shapeList.add(circle); boundedGenericClass.lowerBoundedList = shapeList; List coloredCircles = new ArrayList<>(); ColoredCircle coloredCircle = new ColoredCircle(); coloredCircle.radius = 3.5; coloredCircle.color = "0,0,255"; coloredCircles.add(coloredCircle); boundedGenericClass.upperBoundedList = coloredCircles; HashSet intSet = new HashSet<>(); intSet.add(3); boundedGenericClass.boundedSet = intSet; String expected = "{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}],\"upperBoundedList\":[{\"radius\":3.5,\"color\":\"0,0,255\"}]}"; assertEquals(expected, defaultJsonb.toJson(boundedGenericClass)); Jsonb localJsonb = JsonbBuilder.create(new JsonbConfig()); BoundedGenericClass, Circle> result = localJsonb.fromJson(expected, new TestTypeToken, Circle>>(){}.getType()); assertEquals(Circle.class, result.lowerBoundedList.get(0).getClass()); assertEquals(Double.valueOf(2.5), ((Circle) result.lowerBoundedList.get(0)).getRadius()); //There is no way of identifying precise class (ColoredCircle) during json unmarshalling. //Fields that are missing in upper bounds are skipped. assertEquals(Circle.class, result.upperBoundedList.get(0).getClass()); assertEquals(Double.valueOf(3.5), result.upperBoundedList.get(0).getRadius()); //If it was possible we could assert following, but it is not. //assertEquals("0,0,255", ((ColoredCircle) result.upperBoundedList.get(0)).color); } @Test public void testIncompatibleTypes() { //exception incompatible types assertThrows(ClassCastException.class, () -> { BoundedGenericClass, Circle> otherGeneric = defaultJsonb.fromJson("{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}]}", new TestTypeToken, Circle>>(){}.getType()); HashSet otherIntSet = otherGeneric.boundedSet; Integer intValue = otherIntSet.iterator().next(); }); } @Test public void testMultiLevelGenericExtension() { MultiLevelExtendedGenericTestClass extended = new MultiLevelExtendedGenericTestClass(); extended.field1 = "first"; extended.field2 = 1; String expected = "{\"field1\":\"first\",\"field2\":1}"; assertEquals(expected, defaultJsonb.toJson(extended)); MultiLevelExtendedGenericTestClass result = defaultJsonb.fromJson(expected, MultiLevelExtendedGenericTestClass.class); assertEquals("first", result.field1); assertEquals(Integer.valueOf(1), result.field2); } @Test public void testGenericArray() { GenericArrayClass genericArrayClass = new GenericArrayClass<>(); Number[] numbers = new Number[2]; numbers[0] = 1; numbers[1] = BigDecimal.TEN; genericArrayClass.genericArray = numbers; Integer[] integers = new Integer[2]; integers[0] = 1; integers[1] = 10; genericArrayClass.anotherGenericArray = integers; GenericTestClass genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = Arrays.copyOf(numbers, numbers.length); genericTestClass.field2 = Arrays.copyOf(integers, numbers.length); genericArrayClass.propagatedGenericArray = genericTestClass; String expected = "{\"anotherGenericArray\":[1,10],\"genericArray\":[1,10],\"propagatedGenericArray\":{\"field1\":[1,10],\"field2\":[1,10]}}"; assertEquals(expected, defaultJsonb.toJson(genericArrayClass, new TestTypeToken>(){}.getType())); GenericArrayClass result = defaultJsonb.fromJson(expected, new TestTypeToken>(){}.getType()); assertEquals(BigDecimal.ONE, result.genericArray[0]); assertEquals(BigDecimal.TEN, result.genericArray[1]); assertEquals(Integer.valueOf(1), result.anotherGenericArray[0]); assertEquals(Integer.valueOf(10), result.anotherGenericArray[1]); assertEquals(BigDecimal.ONE, result.propagatedGenericArray.field1[0]); assertEquals(BigDecimal.TEN, result.propagatedGenericArray.field1[1]); assertEquals(Integer.valueOf(1), result.propagatedGenericArray.field2[0]); assertEquals(Integer.valueOf(10), result.propagatedGenericArray.field2[1]); } @Test @SuppressWarnings("unchecked") public void testMarshallRawList() throws ParseException { List rawList = new ArrayList(); final SimpleDateFormat ddMMyyyy = new SimpleDateFormat("ddMMyyyy"); ddMMyyyy.setTimeZone(TimeZone.getTimeZone("Z")); rawList.add(ddMMyyyy.parse("24031981")); Box box = new Box(); box.boxStr = "box string"; box.crate = new Crate(); box.crate.crateStr = "crate str"; rawList.add(box); String result = defaultJsonb.toJson(rawList); assertEquals("[\"1981-03-24T00:00:00Z[UTC]\",{\"boxStr\":\"box string\",\"crate\":{\"crate_str\":\"crate str\"}}]", result); } @Test public void testMultipleBounds() { final LinkedList list = new LinkedList<>(Arrays.asList("Test 1", "Test 2")); MultipleBoundsContainer> container = new MultipleBoundsContainer<>(); container.setInstance(new ArrayList<>()); container.getInstance().add(list); final Type type = new TestTypeToken>>() { }.getType(); String jsonString = defaultJsonb.toJson(container, type); assertEquals("{\"instance\":[[\"Test 1\",\"Test 2\"]]}", jsonString); MultipleBoundsContainer> result = defaultJsonb.fromJson(jsonString, type); assertEquals(container.getInstance(), result.getInstance()); } @Test @SuppressWarnings("unchecked") public void testDeserializeIntoRaw() { GenericTestClass result = defaultJsonb.fromJson("{\"field1\":{\"val1\":\"abc\"},\"field2\":{\"val1\":\"def\"}}", GenericTestClass.class); assertEquals(((HashMap) result.getField1()).get("val1"), "abc"); assertEquals(((HashMap) result.getField2()).get("val1"), "def"); } @Test public void collectionWrapperTest() { CollectionWrapper collectionWrapper = new CollectionWrapper<>(); collectionWrapper.setCollection(new ArrayList<>()); collectionWrapper.setWrappedCollection(new ArrayList<>()); collectionWrapper.setWrappedMap(new HashMap<>()); String s = defaultJsonb.toJson(collectionWrapper); } @Test public void multipleGenericLevels() { FinalMember member = new FinalMember(); member.setName("Jason"); FinalGenericWrapper concreteContainer = new FinalGenericWrapper(); concreteContainer.setMember(member); String expected = "{\"member\":{\"name\":\"Jason\"}}"; Jsonb jsonb = JsonbBuilder.create(); assertEquals(expected, jsonb.toJson(concreteContainer)); FinalGenericWrapper finalGenericWrapper = jsonb.fromJson(expected, FinalGenericWrapper.class); assertEquals(concreteContainer, finalGenericWrapper); } @Test public void lowerBoundTypeVariableInCollectionAttribute() throws Exception { Shape shape = new Shape(); shape.setArea(5D); AnotherGenericTestClass anotherGenericTestClass = new AnotherGenericTestClass<>(); anotherGenericTestClass.field1 = 6; anotherGenericTestClass.field2 = shape; List> asList = Arrays.asList(anotherGenericTestClass); Jsonb jsonb = JsonbBuilder.create(); String toJson = jsonb.toJson(asList); Field field = LowerBoundTypeVariableWithCollectionAttributeClass.class.getDeclaredField("value"); Type genericType = field.getGenericType(); List> fromJson = jsonb.fromJson(toJson, genericType); assertEquals(5, fromJson.get(0).field2.getArea()); assertEquals(6, fromJson.get(0).field1); } @Test public void genericConstructorCreator() { final String expectedJson = "{\"value\":\"Test\"}"; final ConstructorContainer container = new ConstructorContainer<>("Test"); assertEquals(expectedJson, defaultJsonb.toJson(container)); assertEquals(container, defaultJsonb.fromJson(expectedJson, ConstructorContainer.class)); } @Test public void genericStaticCreator() { final String expectedJson = "{\"value\":\"static\"}"; final StaticCreatorContainer container = StaticCreatorContainer.create("static"); assertEquals(expectedJson, defaultJsonb.toJson(container)); assertEquals(container, defaultJsonb.fromJson(expectedJson, StaticCreatorContainer.class)); } @Test public void wildcardCollectionContainer() { final String expectedJson = "{\"collection\":{\"collection\":[{\"wrapped\":\"wrappedElement\"}]}}"; final CollectionContainer collectionContainer = new CollectionContainer(); final CollectionWrapper> collectionWrapper = new CollectionWrapper<>(); final CollectionElement wildcardType = new CollectionElement<>(); wildcardType.setWrapped("wrappedElement"); final Collection> list = List.of(wildcardType); collectionWrapper.setCollection(list); collectionContainer.setCollection(collectionWrapper); assertEquals(expectedJson, defaultJsonb.toJson(collectionContainer)); final CollectionContainer result = defaultJsonb.fromJson(expectedJson, CollectionContainer.class); assertEquals(collectionContainer, result); } @Test public void genericUpperBoundContainer() { final String expectedJson = "{\"tree\":{\"children\":[{\"children\":[],\"name\":\"child\"}],\"name\":\"parent\"}}"; final TreeContainer container = new TreeContainer<>(); final TreeElement parent = new TreeElement("parent"); parent.setChildren(List.of(new TreeElement("child"))); container.setTree(parent); assertEquals(expectedJson, defaultJsonb.toJson(container)); } public interface FunctionalInterface { T getValue(); } public static class ExtendsBigDecimal extends BigDecimal { public ExtendsBigDecimal(String val) { super(val); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractGenericWrapper.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Objects; public abstract class AbstractGenericWrapper { T member; public T getMember() { return member; } public void setMember(T member) { this.member = member; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AbstractGenericWrapper that = (AbstractGenericWrapper) o; return Objects.equals(member, that.member); } @Override public int hashCode() { return Objects.hash(member); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractMember.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; public class AbstractMember { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AnotherGenericTestClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class AnotherGenericTestClass { public T1 field1; public T2 field2; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/BoundedGenericClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.List; import java.util.Set; /** * @author Roman Grigoriadi */ public class BoundedGenericClass, U> { public List upperBoundedList; public List lowerBoundedList; public T boundedSet; public BoundedGenericClass() { } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Circle.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class Circle extends Shape { public Double radius; public Circle() { super(); } public Double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionContainer.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Collection; import java.util.Objects; /** * @author James R. Perkins */ public class CollectionContainer { private CollectionWrapper> collection; public CollectionWrapper> getCollection() { return collection; } public void setCollection(final CollectionWrapper> collection) { this.collection = collection; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof CollectionContainer)) { return false; } final CollectionContainer other = (CollectionContainer) obj; final Collection> thisCollection = collection.getCollection(); final Collection> otherCollection = other.collection.getCollection(); if (thisCollection == null && otherCollection == null) { return true; } if (thisCollection == null || otherCollection == null) { return false; } return thisCollection.containsAll(otherCollection) && otherCollection.containsAll(thisCollection); } @Override public int hashCode() { return Objects.hash(collection); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionElement.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Objects; /** * @author James R. Perkins */ public class CollectionElement { private T wrapped; public T getWrapped() { return wrapped; } public void setWrapped(T wrapped) { this.wrapped = wrapped; } @Override public int hashCode() { return Objects.hash(wrapped); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof CollectionElement)) { return false; } final CollectionElement other = (CollectionElement) obj; return Objects.equals(wrapped, other.wrapped); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionWrapper.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Collection; import java.util.Map; public class CollectionWrapper { public CollectionWrapper() { } private Collection collection; private Collection> wrappedCollection; private Map> wrappedMap; public Collection getCollection() { return collection; } public void setCollection(Collection collection) { this.collection = collection; } public Collection> getWrappedCollection() { return wrappedCollection; } public void setWrappedCollection(Collection> wrappedCollection) { this.wrappedCollection = wrappedCollection; } public Map> getWrappedMap() { return wrappedMap; } public void setWrappedMap(Map> wrappedMap) { this.wrappedMap = wrappedMap; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ColoredCircle.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class ColoredCircle extends Circle { public String color; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ConstructorContainer.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Objects; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author James R. Perkins */ public class ConstructorContainer { private final T value; @JsonbCreator public ConstructorContainer(@JsonbProperty("value") final T value) { this.value = value; } public T getValue() { return value; } @Override public String toString() { return "ConstructorContainer[value=" + value + "]"; } @Override public int hashCode() { return Objects.hash(value); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof ConstructorContainer)) { return false; } final ConstructorContainer other = (ConstructorContainer) obj; return Objects.equals(value, other.value); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CyclicSubClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class CyclicSubClass extends MyCyclicGenericClass { public String subField; public CyclicSubClass() { } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ExtendedGenericTestClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class ExtendedGenericTestClass extends GenericTestClass { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalGenericWrapper.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; public class FinalGenericWrapper extends MiddleGenericWrapper { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalMember.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Objects; public class FinalMember extends AbstractMember { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FinalMember that = (FinalMember) o; return Objects.equals(name, that.name); } @Override public int hashCode() { return Objects.hash(name); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericArrayClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class GenericArrayClass { public T[] genericArray; public U[] anotherGenericArray; public GenericTestClass propagatedGenericArray; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericTestClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * Test class used in generics tests. * * @author Dmitry Kornilov */ public class GenericTestClass { public T field1; public U field2; public GenericTestClass() {} public T getField1() { return field1; } public void setField1(T field1) { this.field1 = field1; } public U getField2() { return field2; } public void setField2(U field2) { this.field2 = field2; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericWithUnboundedWildcardClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.List; /** * @author Roman Grigoriadi */ public class GenericWithUnboundedWildcardClass { public List wildcardList; public GenericWithUnboundedWildcardClass() { } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/LowerBoundTypeVariableWithCollectionAttributeClass.java ================================================ /* * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Collection; /** * @author Alessandro Moscatelli */ public class LowerBoundTypeVariableWithCollectionAttributeClass { private Collection> value; public Collection> getValue() { return value; } public void setValue(Collection> value) { this.value = value; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MiddleGenericWrapper.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; public abstract class MiddleGenericWrapper extends AbstractGenericWrapper { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultiLevelExtendedGenericTestClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class MultiLevelExtendedGenericTestClass extends ExtendedGenericTestClass { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.List; import java.util.Queue; public class MultipleBoundsContainer implements TypeContainer> { protected List instance; @Override public List getInstance() { return instance; } @Override public void setInstance(List instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MyCyclicGenericClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class MyCyclicGenericClass> { public T field1; public MyCyclicGenericClass() { } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/PropagatedGenericClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.List; /** * @author Roman Grigoriadi */ public class PropagatedGenericClass { public List, X>> genericList; public GenericTestClass genericTestClass; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ScalarValueWrapper.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class ScalarValueWrapper { private T value; public ScalarValueWrapper() { } public ScalarValueWrapper(T value) { this.value = value; } public T getValue() { return value; } public void setValue(T value) { this.value = value; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Shape.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class Shape { private Double area; public Shape() { } public Double getArea() { return area; } public void setArea(Double area) { this.area = area; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/StaticCreatorContainer.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.Objects; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; /** * @author James R. Perkins */ public class StaticCreatorContainer { private final T value; private StaticCreatorContainer(T value) { this.value = value; } @JsonbCreator public static StaticCreatorContainer create(@JsonbProperty("value") final T value) { return new StaticCreatorContainer<>(value); } public T getValue() { return value; } @Override public String toString() { return "StaticCreatorContainer[value=" + value + "]"; } @Override public int hashCode() { return Objects.hash(value); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof StaticCreatorContainer)) { return false; } final StaticCreatorContainer other = (StaticCreatorContainer) obj; return Objects.equals(value, other.value); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TreeContainer.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author James R. Perkins */ public class TreeContainer> { private TreeTypeContainer tree; public TreeTypeContainer getTree() { return tree; } public void setTree(final TreeTypeContainer tree) { this.tree = tree; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TreeElement.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.ArrayList; import java.util.List; /** * @author James R. Perkins */ public class TreeElement implements TreeTypeContainer { private final String name; private List children = new ArrayList<>(); public TreeElement(final String name) { this.name = name; } public String getName() { return name; } public List getChildren() { return children; } public void setChildren(final List children) { this.children = children; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TreeTypeContainer.java ================================================ /* * Copyright (c) 2025 Red Hat, Inc. and/or its affiliates. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.util.List; /** * @author James R. Perkins */ public interface TreeTypeContainer> { List getChildren(); void setChildren(List children); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TypeContainer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; public interface TypeContainer { T getInstance(); void setInstance(T instance); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildCardClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; /** * @author Roman Grigoriadi */ public class WildCardClass { public T number; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildcardMultipleBoundsClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.generics.model; import java.io.Serializable; import java.util.List; /** * @author Roman Grigoriadi */ public class WildcardMultipleBoundsClass> { public T wildcardField; public GenericTestClass genericTestClassPropagatedWildCard; public List propagatedWildcardList; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/InheritanceTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass; import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass; import org.eclipse.yasson.defaultmapping.inheritance.model.PartialOverride; import org.eclipse.yasson.defaultmapping.inheritance.model.PropertyOrderSecond; import org.eclipse.yasson.defaultmapping.inheritance.model.SecondLevel; import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ExtendsExtendsPropagatedGenericClass; import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ExtendsPropagatedGenericClass; import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ImplementsGenericInterfaces; import org.eclipse.yasson.defaultmapping.inheritance.model.generics.SecondLevelGeneric; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * Tests inheritance model marshalling / unmarshalling * * Tests property order, method overloading, generic type ({@link java.lang.reflect.TypeVariable}) resolving for unmarshalling. * * @author Roman Grigoriadi */ public class InheritanceTest { @Test public void testBasicInheritance() throws Exception { SecondLevel secondLevel = new SecondLevel(); secondLevel.setInFirstLevel("IN_FIRST_LEVEL"); secondLevel.setInSecondLevel("IN_SECOND_LEVEL"); secondLevel.setInZeroOverriddenInFirst("IN_ZERO_OVERRIDDEN_IN_FIRST"); String json = "{\"inZeroOverriddenInFirst\":\"IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inFirstLevel\":\"IN_FIRST_LEVEL\",\"inSecondLevel\":\"IN_SECOND_LEVEL\"}"; assertEquals(json, defaultJsonb.toJson(secondLevel)); SecondLevel result = defaultJsonb.fromJson(json, SecondLevel.class); assertEquals("IN_FIRST_LEVEL", result.getInFirstLevel()); assertEquals("IN_SECOND_LEVEL", result.getInSecondLevel()); assertEquals("IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst()); } @Test public void testBasicGenericInheritance() { SecondLevelGeneric secondLevelGeneric = new SecondLevelGeneric<>(); secondLevelGeneric.setInSecondLevel(BigDecimal.TEN); secondLevelGeneric.setInFirstLevel((short) 255); secondLevelGeneric.setInZeroOverriddenInFirst("IN_ZERO_OVERRIDDEN_IN_FIRST"); secondLevelGeneric.setInZero("IN_ZERO"); String json = "{\"inZero\":\"IN_ZERO\",\"inFirstLevel\":255,\"inZeroOverriddenInFirst\":\"IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inSecondLevel\":10}"; assertEquals(json, defaultJsonb.toJson(secondLevelGeneric)); SecondLevelGeneric result = defaultJsonb.fromJson(json, new TestTypeToken>(){}.getType()); assertEquals(BigDecimal.TEN, result.getInSecondLevel()); assertEquals(Short.valueOf("255"), result.getInFirstLevel()); assertEquals("IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst()); assertEquals("IN_ZERO", result.getInZero()); } @Test public void testPropagatedGenericInheritance() throws Exception { List stringList = new ArrayList<>(); stringList.add("first"); stringList.add("second"); GenericTestClass, BigDecimal> genericInList = new GenericTestClass<>(); genericInList.field1 = stringList; genericInList.field2 = BigDecimal.TEN; List, BigDecimal>> listWithGenerics = new ArrayList<>(); listWithGenerics.add(genericInList); GenericTestClass genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = "GENERIC_STRING"; genericTestClass.field2 = BigDecimal.ONE; ExtendsExtendsPropagatedGenericClass underTest = new ExtendsExtendsPropagatedGenericClass(); underTest.genericTestClass = genericTestClass; underTest.genericList = listWithGenerics; String json = "{\"genericList\":[{\"field1\":[\"first\",\"second\"],\"field2\":10}],\"genericTestClass\":{\"field1\":\"GENERIC_STRING\",\"field2\":1}}"; assertEquals(json, defaultJsonb.toJson(underTest)); ExtendsExtendsPropagatedGenericClass result = defaultJsonb.fromJson(json, ExtendsExtendsPropagatedGenericClass.class); assertEquals(GenericTestClass.class, result.genericList.get(0).getClass()); assertEquals("first", result.genericList.get(0).field1.get(0)); assertEquals("second", result.genericList.get(0).field1.get(1)); assertEquals(BigDecimal.TEN, result.genericList.get(0).field2); assertEquals(GenericTestClass.class, result.genericTestClass.getClass()); assertEquals("GENERIC_STRING", result.genericTestClass.field1); assertEquals(BigDecimal.ONE, result.genericTestClass.field2); } @Test public void testPropagatedGenericInheritance1() throws Exception { List stringList = new ArrayList<>(); stringList.add("first"); stringList.add("second"); GenericTestClass, BigDecimal> genericInList = new GenericTestClass<>(); genericInList.field1 = stringList; genericInList.field2 = BigDecimal.TEN; List, BigDecimal>> listWithGenerics = new ArrayList<>(); listWithGenerics.add(genericInList); GenericTestClass genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = "SECOND_LEVEL_GENERIC_STRING"; genericTestClass.field2 = BigDecimal.ONE; PropagatedGenericClass propagatedGenericClass = new PropagatedGenericClass<>(); propagatedGenericClass.genericList = listWithGenerics; propagatedGenericClass.genericTestClass = genericTestClass; stringList = new ArrayList<>(); stringList.add("third"); stringList.add("fourth"); genericInList = new GenericTestClass<>(); genericInList.field1 = stringList; genericInList.field2 = BigDecimal.ZERO; listWithGenerics = new ArrayList<>(); listWithGenerics.add(genericInList); genericTestClass = new GenericTestClass<>(); genericTestClass.field1 = "FIRST_LEVEL_GENERIC_STRING"; genericTestClass.field2 = new BigDecimal("11"); ExtendsPropagatedGenericClass extendsPropagatedGenericClass = new ExtendsPropagatedGenericClass<>(); extendsPropagatedGenericClass.genericList = listWithGenerics; extendsPropagatedGenericClass.genericTestClass = genericTestClass; SecondLevelGeneric, ExtendsPropagatedGenericClass, String> secondLevelGeneric = new SecondLevelGeneric<>(); secondLevelGeneric.setInSecondLevel(propagatedGenericClass); secondLevelGeneric.setInFirstLevel(extendsPropagatedGenericClass); secondLevelGeneric.setInZeroOverriddenInFirst("STRING_IN_ZERO_OVERRIDDEN_IN_FIRST"); secondLevelGeneric.setInZero("IN_ZERO"); String json = "{\"inZero\":\"IN_ZERO\",\"inFirstLevel\":{\"genericList\":[{\"field1\":[\"third\",\"fourth\"],\"field2\":0}],\"genericTestClass\":{\"field1\":\"FIRST_LEVEL_GENERIC_STRING\",\"field2\":11}},\"inZeroOverriddenInFirst\":\"STRING_IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inSecondLevel\":{\"genericList\":[{\"field1\":[\"first\",\"second\"],\"field2\":10}],\"genericTestClass\":{\"field1\":\"SECOND_LEVEL_GENERIC_STRING\",\"field2\":1}}}"; final Type runtimeType = new TestTypeToken, ExtendsPropagatedGenericClass, String>>(){}.getType(); assertEquals(json, defaultJsonb.toJson(secondLevelGeneric, runtimeType)); SecondLevelGeneric, ExtendsPropagatedGenericClass, String> result = defaultJsonb.fromJson(json, runtimeType); assertEquals("first", result.getInSecondLevel().genericList.get(0).field1.get(0)); assertEquals("second", result.getInSecondLevel().genericList.get(0).field1.get(1)); assertEquals(BigDecimal.TEN, result.getInSecondLevel().genericList.get(0).field2); assertEquals("SECOND_LEVEL_GENERIC_STRING", result.getInSecondLevel().genericTestClass.field1); assertEquals(BigDecimal.ONE, result.getInSecondLevel().genericTestClass.field2); assertEquals("third", result.getInFirstLevel().genericList.get(0).field1.get(0)); assertEquals("fourth", result.getInFirstLevel().genericList.get(0).field1.get(1)); assertEquals(BigDecimal.ZERO, result.getInFirstLevel().genericList.get(0).field2); assertEquals("FIRST_LEVEL_GENERIC_STRING", result.getInFirstLevel().genericTestClass.field1); assertEquals(new BigDecimal("11"), result.getInFirstLevel().genericTestClass.field2); assertEquals("STRING_IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst()); assertEquals("IN_ZERO", result.getInZero()); } @Test public void testInterfaceGenericInheritance() throws Exception { ImplementsGenericInterfaces implementsGenericInterfaces = new ImplementsGenericInterfaces<>(); implementsGenericInterfaces.setGenericValue("GENERIC_VALUE"); implementsGenericInterfaces.setAnotherGenericValue(255); String json = "{\"anotherGenericValue\":255,\"genericValue\":\"GENERIC_VALUE\"}"; assertEquals(json, defaultJsonb.toJson(implementsGenericInterfaces)); ImplementsGenericInterfaces result = defaultJsonb.fromJson(json, new TestTypeToken>(){}.getType()); assertEquals("GENERIC_VALUE", result.getGenericValue()); assertEquals(Integer.valueOf(255), result.getAnotherGenericValue()); } @Test public void testPartialOverride() { PartialOverride partialOverride = new PartialOverride(); partialOverride.setIntValue(5); partialOverride.setStrValue("abc"); String json = defaultJsonb.toJson(partialOverride); assertEquals("{\"intValue\":5,\"strValue\":\"abc\"}", json); PartialOverride result = defaultJsonb.fromJson("{\"intValue\":5,\"strValue\":\"abc\"}", PartialOverride.class); assertEquals(5, result.getIntValue()); assertEquals("abc", result.getStrValue()); } @Test public void testPropOrderPartiallyOverriddenProperty() { PropertyOrderSecond pojo = new PropertyOrderSecond(); pojo.setZero("ZERO"); pojo.setZeroPartiallyOverriddenInFirst("ZERO_PARTIALLY_OVERRIDDEN_IN_FIRST"); pojo.setZeroOverriddenInSecond("ZERO_OVERRIDDEN_IN_SECOND"); pojo.setFirst("FIRST"); pojo.setSecond("SECOND"); String result = defaultJsonb.toJson(pojo); assertEquals("{\"zero\":\"ZERO\",\"zeroPartiallyOverriddenInFirst\":\"ZERO_PARTIALLY_OVERRIDDEN_IN_FIRST\",\"first\":\"FIRST\",\"second\":\"SECOND\",\"zeroOverriddenInSecond\":\"ZERO_OVERRIDDEN_IN_SECOND\"}", result); } @Test public void testInheritanceSerialization() { AnimalWrapper animalWrapper = new AnimalWrapper(); animalWrapper.animal = new Dog(); //Just initialize serializer cache for Animal and Dog defaultJsonb.toJson(animalWrapper); //Check if the Dog instance is dynamically resolved even though Dog serializer has been created before DogWrapper dogWrapper = new DogWrapper(); dogWrapper.dog = new Dog(); assertEquals("{\"dog\":{\"isDog\":true}}", defaultJsonb.toJson(dogWrapper)); dogWrapper.dog = new SmallDog(); assertEquals("{\"dog\":{\"isDog\":true,\"isSmallDog\":true}}", defaultJsonb.toJson(dogWrapper)); } public static class AnimalWrapper { public Animal animal; } public static class DogWrapper { public Dog dog; } public static class Animal { } public static class Dog extends Animal { public boolean isDog = true; } public static class SmallDog extends Dog { public boolean isSmallDog = true; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/AbstractZeroLevel.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public abstract class AbstractZeroLevel { protected String inZeroOverriddenInFirst; abstract String getInZeroOverriddenInFirst(); abstract void setInZeroOverriddenInFirst(String value); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/FirstLevel.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class FirstLevel extends AbstractZeroLevel { private String inFirstLevel; @Override public String getInZeroOverriddenInFirst() { return inZeroOverriddenInFirst; } @Override public void setInZeroOverriddenInFirst(String value) { inZeroOverriddenInFirst = value; } public String getInFirstLevel() { return inFirstLevel; } public void setInFirstLevel(String inFirstLevel) { this.inFirstLevel = inFirstLevel; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverride.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * Bias property is not readable by putting field and getter in other class than setter. * * @author Roman Grigoriadi */ public class PartialOverride extends PartialOverrideBase { @Override public void setIntValue(int intValue) { super.setIntValue(intValue); } @Override public void setStrValue(String strValue) { super.setStrValue(strValue); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverrideBase.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class PartialOverrideBase { private int intValue; private String strValue; public int getIntValue() { return intValue; } public void setIntValue(int intValue) { this.intValue = intValue; } public String getStrValue() { return strValue; } public void setStrValue(String strValue) { this.strValue = strValue; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderFirst.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class PropertyOrderFirst extends PropertyOrderZero { private String first; public String getFirst() { return first; } public void setFirst(String first) { this.first = first; } @Override public void setZeroPartiallyOverriddenInFirst(String zeroPartiallyOverriddenInFirst) { super.setZeroPartiallyOverriddenInFirst(zeroPartiallyOverriddenInFirst); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderSecond.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class PropertyOrderSecond extends PropertyOrderFirst { private String zeroOverridden; private String second; public String getSecond() { return second; } public void setSecond(String second) { this.second = second; } @Override public String getZeroOverriddenInSecond() { return zeroOverridden; } @Override public void setZeroOverriddenInSecond(String zeroOverriddenInSecond) { this.zeroOverridden = zeroOverriddenInSecond; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderZero.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class PropertyOrderZero { private String zeroOverriddenInSecond; //only setter is overridden private String zeroPartiallyOverriddenInFirst; private String zero; public String getZero() { return zero; } public void setZero(String zero) { this.zero = zero; } public String getZeroOverriddenInSecond() { return zeroOverriddenInSecond; } public void setZeroOverriddenInSecond(String zeroOverriddenInSecond) { this.zeroOverriddenInSecond = zeroOverriddenInSecond; } public String getZeroPartiallyOverriddenInFirst() { return zeroPartiallyOverriddenInFirst; } public void setZeroPartiallyOverriddenInFirst(String zeroPartiallyOverriddenInFirst) { this.zeroPartiallyOverriddenInFirst = zeroPartiallyOverriddenInFirst; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/SecondLevel.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model; /** * @author Roman Grigoriadi */ public class SecondLevel extends FirstLevel { private String inSecondLevel; //hides inFirstLevel in FirstLevelGeneric private String inFirstLevel; public String getInSecondLevel() { return inSecondLevel; } public void setInSecondLevel(String inSecondLevel) { this.inSecondLevel = inSecondLevel; } @Override public String getInFirstLevel() { return inFirstLevel; } @Override public void setInFirstLevel(String inFirstLevel) { this.inFirstLevel = inFirstLevel; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AbstractZeroLevelGeneric.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public abstract class AbstractZeroLevelGeneric { private Z inZero; protected ZF inZeroOverriddenInFirst; abstract ZF getInZeroOverriddenInFirst(); abstract void setInZeroOverriddenInFirst(ZF value); public Z getInZero() { return inZero; } public void setInZero(Z inZero) { this.inZero = inZero; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AnotherGenericInterface.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public interface AnotherGenericInterface { T getAnotherGenericValue(); void setAnotherGenericValue(T value); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsExtendsPropagatedGenericClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; import java.math.BigDecimal; /** * @author Roman Grigoriadi */ public class ExtendsExtendsPropagatedGenericClass extends ExtendsPropagatedGenericClass { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsPropagatedGenericClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass; /** * @author Roman Grigoriadi */ public class ExtendsPropagatedGenericClass extends PropagatedGenericClass { } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/FirstLevelGeneric.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public class FirstLevelGeneric extends AbstractZeroLevelGeneric { private F inFirstLevel; @Override public Z getInZeroOverriddenInFirst() { return inZeroOverriddenInFirst; } @Override public void setInZeroOverriddenInFirst(Z value) { inZeroOverriddenInFirst = value; } public F getInFirstLevel() { return inFirstLevel; } public void setInFirstLevel(F inFirstLevel) { this.inFirstLevel = inFirstLevel; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/GenericInterface.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public interface GenericInterface { T getGenericValue(); void setGenericValue(T value); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ImplementsGenericInterfaces.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public class ImplementsGenericInterfaces implements GenericInterface, AnotherGenericInterface { private A a; private B b; @Override public B getAnotherGenericValue() { return b; } @Override public void setAnotherGenericValue(B value) { this.b = value; } @Override public A getGenericValue() { return a; } @Override public void setGenericValue(A value) { this.a = value; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/SecondLevelGeneric.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.inheritance.model.generics; /** * @author Roman Grigoriadi */ public class SecondLevelGeneric extends FirstLevelGeneric { private S inSecondLevel; public S getInSecondLevel() { return inSecondLevel; } public void setInSecondLevel(S inSecondLevel) { this.inSecondLevel = inSecondLevel; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpLong.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.jsonp; import jakarta.json.JsonNumber; import java.math.BigDecimal; import java.math.BigInteger; /** * Test class implementing {@link JsonNumber} interface. * * @author Dmitry Kornilov */ final public class JsonpLong implements JsonNumber { private final long num; private BigDecimal bigDecimal; // assigning it lazily on demand public JsonpLong(long num) { this.num = num; } @Override public int intValue() { return bigDecimalValue().intValue(); } @Override public int intValueExact() { return bigDecimalValue().intValueExact(); } @Override public BigInteger bigIntegerValue() { return bigDecimalValue().toBigInteger(); } @Override public BigInteger bigIntegerValueExact() { return bigDecimalValue().toBigIntegerExact(); } @Override public boolean isIntegral() { return true; } @Override public long longValue() { return num; } @Override public long longValueExact() { return num; } @Override public double doubleValue() { return num; } @Override public BigDecimal bigDecimalValue() { // reference assignments are atomic. At the most some more temp // BigDecimal objects are created BigDecimal bd = bigDecimal; if (bd == null) { bigDecimal = bd = new BigDecimal(num); } return bd; } @Override public ValueType getValueType() { return ValueType.NUMBER; } @Override public String toString() { return Long.toString(num); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpString.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.jsonp; import jakarta.json.JsonString; /** * Test class implementing {@link JsonString} interface. * * @author Dmitry Kornilov */ public class JsonpString implements JsonString { private final String value; public JsonpString(String value) { this.value = value; } @Override public String getString() { return value; } @Override public CharSequence getChars() { return value; } @Override public ValueType getValueType() { return ValueType.STRING; } @Override public int hashCode() { return value.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof JsonString)) { return false; } JsonString other = (JsonString) obj; return getString().equals(other.getString()); } @Override public String toString() { return "\"" + value + "\""; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.jsonp; import java.math.BigDecimal; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonBuilderFactory; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonString; import jakarta.json.JsonValue; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; import org.eclipse.yasson.defaultmapping.jsonp.model.JsonpPojo; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Default mapping JSONP integration tests. * * @author Dmitry Kornilov */ public class JsonpTest { public static class JsonValueWrapper { public JsonValue jsonValue; public JsonValueWrapper(JsonValue jsonValue) { this.jsonValue = jsonValue; } public JsonValueWrapper() { } } @Test public void testInnerJsonObject() { final JsonBuilderFactory factory = Json.createBuilderFactory(null); final JsonObject jsonObject = factory.createObjectBuilder() .add("name", "home") .add("city", "Prague") .build(); final JsonObjectBuilder customerBuilder = factory.createObjectBuilder(); customerBuilder.add("f1", "abc123"); customerBuilder.add("f2", BigDecimal.TEN); customerBuilder.add("f3", 12); customerBuilder.add("city", jsonObject); final JsonObjectBuilder wrapperBuilder = factory.createObjectBuilder(); wrapperBuilder.add("f1", "abc"); wrapperBuilder.add("cust", customerBuilder); final JsonObject wrapper = wrapperBuilder.build(); String expected = "{\"f1\":\"abc\",\"cust\":{\"f1\":\"abc123\",\"f2\":10,\"f3\":12,\"city\":{\"name\":\"home\",\"city\":\"Prague\"}}}"; assertEquals(expected, defaultJsonb.toJson(wrapper)); JsonObject result = defaultJsonb.fromJson(expected, JsonObject.class); assertEquals("home", result.getJsonObject("cust").getJsonObject("city").getString("name")); assertEquals("abc123", result.getJsonObject("cust").getString("f1")); assertEquals("abc123", result.getJsonObject("cust").getString("f1")); } @Test public void testMarshallJsonArray() { final JsonBuilderFactory factory = Json.createBuilderFactory(null); final JsonArray jsonArray = factory.createArrayBuilder() .add(1) .add(2) .build(); assertEquals("{\"jsonValue\":[1,2]}", defaultJsonb.toJson(new JsonValueWrapper(jsonArray))); } @Test public void testMarshallJsonValue() { assertEquals("{\"jsonValue\":true}", defaultJsonb.toJson(new JsonValueWrapper(JsonValue.TRUE))); } @Test public void testMarshallJsonNumber() { assertEquals("{\"jsonValue\":10}", defaultJsonb.toJson(new JsonValueWrapper(new JsonpLong(10)))); } @Test public void testMarshallJsonString() { assertEquals("{\"jsonValue\":\"hello\"}", defaultJsonb.toJson(new JsonValueWrapper(new JsonpString("hello")))); } @Test public void testJsonPojo() { JsonbConfig config = new JsonbConfig(); // config.withFormatting(true); Jsonb jsonb = JsonbBuilder.create(config); JsonpPojo pojo = new JsonpPojo(); final JsonObjectBuilder obj1builder = JsonProvider.provider().createObjectBuilder(); obj1builder.add("strVal", "string value"); obj1builder.add("numVal", 2.0d); obj1builder.addNull("nullVal"); obj1builder.add("boolVal", Boolean.TRUE); final JsonObjectBuilder obj2Builder = JsonProvider.provider().createObjectBuilder(); obj2Builder.add("innerStr", "string val"); obj2Builder.add("innerNum", 11.1d); final JsonObject obj2 = obj2Builder.build(); JsonArrayBuilder array1Builder = JsonProvider.provider().createArrayBuilder(); array1Builder.addNull().add(false).add(11L).add(BigDecimal.TEN).add("array STR value").add(obj2); JsonArray jsonArray1 = array1Builder.build(); obj1builder.add("innerJsonObject", obj2); obj1builder.add("innerArrayObject", jsonArray1); final JsonObject obj1 = obj1builder.build(); pojo.jsonObject = obj1; JsonArrayBuilder arrayBuilder = JsonProvider.provider().createArrayBuilder(); arrayBuilder.add(obj1).add(true).add(obj2).add(101.0d).add(BigDecimal.TEN); pojo.jsonArray = arrayBuilder.build(); String expected = "{\"jsonArray\":[{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]},true,{\"innerStr\":\"string val\",\"innerNum\":11.1},101.0,10],\"jsonObject\":{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]}}"; final String actual = jsonb.toJson(pojo); assertEquals(expected, actual); JsonpPojo result = jsonb.fromJson(expected, JsonpPojo.class); assertEquals(pojo.jsonObject, result.jsonObject); assertEquals(pojo.jsonArray, result.jsonArray); } @Test public void testJsonObject() { final JsonObjectBuilder objBuilder = JsonProvider.provider().createObjectBuilder(); objBuilder.add("boolTrue", Boolean.TRUE).add("boolFalse", Boolean.FALSE) .addNull("null").add("str", "String"); JsonArrayBuilder arrBuilder = JsonProvider.provider().createArrayBuilder(); arrBuilder.add(11L).add(Boolean.FALSE).add(BigDecimal.TEN); objBuilder.add("array", arrBuilder); JsonObject object = objBuilder.build(); String expected = "{\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"str\":\"String\",\"array\":[11,false,10]}"; assertEquals(expected, defaultJsonb.toJson(object)); JsonObject result = defaultJsonb.fromJson(expected, JsonObject.class); assertEquals(object, result); } @Test public void testJsonArray() { JsonArrayBuilder arrBuilder = JsonProvider.provider().createArrayBuilder(); arrBuilder.add(11L).add(Boolean.FALSE).add(BigDecimal.TEN); JsonObjectBuilder objBuilder = JsonProvider.provider().createObjectBuilder(); objBuilder.add("boolTrue", Boolean.TRUE).add("boolFalse", Boolean.FALSE) .addNull("null").add("str", "String"); arrBuilder.add(objBuilder); JsonArray arr = arrBuilder.build(); String expected = "[11,false,10,{\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"str\":\"String\"}]"; assertEquals(expected, defaultJsonb.toJson(arr)); JsonArray result = defaultJsonb.fromJson(expected, JsonArray.class); assertEquals(arr, result); } @Test public void testJsonObjectAsValue() { final JsonValueWrapper jsonValueWrapper = defaultJsonb.fromJson("{ \"jsonValue\" : { \"stringInstance\" : \"Test String\" } }", JsonValueWrapper.class); assertEquals("Test String", ((JsonObject) jsonValueWrapper.jsonValue).getString("stringInstance")); } @Test public void testJsonValueString() { JsonValueWrapper pojo = new JsonValueWrapper(Json.createValue("abc")); String json = defaultJsonb.toJson(pojo); assertEquals("{\"jsonValue\":\"abc\"}", json); JsonValueWrapper result = defaultJsonb.fromJson("{\"jsonValue\":\"def\"}", JsonValueWrapper.class); assertTrue(result.jsonValue instanceof JsonString); assertEquals("def", ((JsonString)result.jsonValue).getString()); } @Test public void testJsonValueAsObject() { JsonObject build = Json.createObjectBuilder().add("prop1", "val1") .add("prop2", "val2") .add("innerObj1", Json.createObjectBuilder().add("inner1", "innerVal1").build()) .build(); JsonValueWrapper pojo = new JsonValueWrapper(build); String expected = "{\"jsonValue\":{\"prop1\":\"val1\",\"prop2\":\"val2\",\"innerObj1\":{\"inner1\":\"innerVal1\"}}}"; String json = defaultJsonb.toJson(pojo); assertEquals(expected, json); JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class); assertTrue(result.jsonValue instanceof JsonObject); JsonObject jsonObject = (JsonObject) result.jsonValue; assertEquals("val1", jsonObject.getString("prop1")); assertEquals("innerVal1", jsonObject.getJsonObject("innerObj1").getString("inner1")); } @Test public void testJsonValueAsArray() { JsonArray jsonArray = Json.createArrayBuilder().add(1).add(2).add(3).add(Json.createObjectBuilder().add("a","b").build()).build(); JsonValueWrapper pojo = new JsonValueWrapper(jsonArray); String expected = "{\"jsonValue\":[1,2,3,{\"a\":\"b\"}]}"; String json = defaultJsonb.toJson(pojo); assertEquals(expected, json); JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class); assertTrue(result.jsonValue instanceof JsonArray); JsonArray resultArray = (JsonArray) result.jsonValue; assertEquals(1, resultArray.getInt(0)); assertEquals(2, resultArray.getInt(1)); assertEquals(3, resultArray.getInt(2)); assertEquals("b", resultArray.getJsonObject(3).getString("a")); } @Test public void testJsonNullValue() { JsonValueWrapper pojo = new JsonValueWrapper(null); String expected = "{}"; String json = defaultJsonb.toJson(pojo); assertThat(json, is(expected)); JsonValueWrapper deserialized = defaultJsonb.fromJson(expected, JsonValueWrapper.class); assertThat(deserialized.jsonValue, nullValue()); deserialized = defaultJsonb.fromJson("{\"jsonValue\":null}", JsonValueWrapper.class); assertThat(deserialized.jsonValue, is(JsonValue.NULL)); } @Test public void testJsonpNullValues() { JsonpPojo pojo = new JsonpPojo(); String expected = "{}"; String nullValues = "{\"jsonObject\":null, \"jsonArray\":null, \"jsonNumber\":null, \"jsonString\":null, " + "\"jsonValue\":null }"; String json = defaultJsonb.toJson(pojo); assertThat(json, is(expected)); JsonpPojo deserialized = defaultJsonb.fromJson(expected, JsonpPojo.class); assertThat(deserialized.jsonObject, nullValue()); deserialized = defaultJsonb.fromJson(nullValues, JsonpPojo.class); assertThat(deserialized.jsonObject, nullValue()); assertThat(deserialized.jsonArray, nullValue()); assertThat(deserialized.jsonNumber, nullValue()); assertThat(deserialized.jsonString, nullValue()); assertThat(deserialized.jsonValue, is(JsonValue.NULL)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/jsonp/model/JsonpPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.jsonp.model; import jakarta.json.*; /** * @author Roman Grigoriadi */ public class JsonpPojo { public JsonObject jsonObject; public JsonArray jsonArray; public JsonNumber jsonNumber; public JsonString jsonString; public JsonValue jsonValue; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/lambda/Addressable.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.lambda; /** * A functional interface with no default properties. */ public interface Addressable { /** * Public readable property. * * @return name to be addressed with */ String getName(); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/lambda/Cat.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.lambda; /** * Class used to control serialization of lambda expression generated from functional interfaces with defaults. */ public class Cat implements Pet { private String name; Cat(String name) { this.name = name; } @Override public String getName() { return this.name; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/lambda/LambdaExpressionTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.lambda; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; /** * Test marshalling objects generated using lambda expressions. * * Scenarios test lambda-generated objects serialization against concrete class objects serialization. It is thus * assumed that concrete class behaviour is tested successfully elsewhere. */ public class LambdaExpressionTest { @Test public void testMarshallFunctionalInterface() { String name = "WALL-E"; Addressable control = new Robot(name); Addressable lambda = () -> name; assertEquals(defaultJsonb.toJson(control), defaultJsonb.toJson(lambda)); } @Test public void testMarshallFunctionalInterfaceWithDefaultProperties() { String name = "Cheshire"; Pet control = new Cat(name); Pet lambda = () -> name; assertEquals(defaultJsonb.toJson(control), defaultJsonb.toJson(lambda)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/lambda/Pet.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.lambda; /** * A functional interface with one default property. */ public interface Pet extends Addressable { /** * Public readable property with default value. * * @return pet's age in months */ default int getAge() { return 0; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/lambda/Robot.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.lambda; /** * Class used to control serialization of lambda expression generated from functional interfaces with no defaults. */ public class Robot implements Addressable { private String name; Robot(String name) { this.name = name; } @Override public String getName() { return this.name; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.Assertions; import org.eclipse.yasson.defaultmapping.modifiers.model.ChildOfPackagePrivateParent; /** * Test access modifiers on classes * * @author David Kral */ public class ClassModifiersTest { @Test public void testPackagePrivateParent() { ChildOfPackagePrivateParent child = new ChildOfPackagePrivateParent(); child.id = 1; child.name = "SomeName"; String json = defaultJsonb.toJson(child); assertEquals("{\"id\":1,\"name\":\"SomeName\"}", json); ChildOfPackagePrivateParent result = defaultJsonb.fromJson(json, ChildOfPackagePrivateParent.class); assertEquals(child.id, result.id); assertEquals(child.name, result.name); } class NestedPackageParent { public int id; } public class NestedPackageChild extends NestedPackageParent { public String name; } @Test public void testNestedPackagePrivateParent() { NestedPackageChild child = new NestedPackageChild(); child.id = 1; child.name = "SomeName"; Assertions.shouldFail(() -> defaultJsonb.toJson(child), msg -> msg.contains("java.lang.IllegalAccessException") && msg.contains("Error accessing field 'id' declared in " + "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedPackageParent'")); } private class NestedPrivateParent { public int id; } public class NestedPrivateChild extends NestedPrivateParent { public String name; } @Test public void testNestedPrivateParent() { NestedPrivateChild child = new NestedPrivateChild(); child.id = 1; child.name = "SomeName"; Assertions.shouldFail(() -> defaultJsonb.toJson(child), msg -> msg.contains("java.lang.IllegalAccessException") && msg.contains("Error accessing field 'id' declared in " + "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedPrivateParent'")); } static class NestedStaticPackageParent { public int id; } public static class NestedStaticPackageChild extends NestedStaticPackageParent { public String name; } @Test public void testNestedStaticPackagePrivateParent() { NestedStaticPackageChild child = new NestedStaticPackageChild(); child.id = 1; child.name = "SomeName"; Assertions.shouldFail(() -> defaultJsonb.toJson(child), msg -> msg.contains("java.lang.IllegalAccessException") && msg.contains("Error accessing field 'id' declared in " + "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedStaticPackageParent'")); } private static class NestedStaticPrivateParent { public int id; } public static class NestedStaticPrivateChild extends NestedStaticPrivateParent { public String name; } @Test public void testNestedStaticPrivateParent() { NestedStaticPrivateChild child = new NestedStaticPrivateChild(); child.id = 1; child.name = "SomeName"; Assertions.shouldFail(() -> defaultJsonb.toJson(child), msg -> msg.contains("java.lang.IllegalAccessException") && msg.contains("Error accessing field 'id' declared in " + "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedStaticPrivateParent'")); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/DefaultMappingModifiersTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.defaultmapping.modifiers.model.Person; import org.eclipse.yasson.defaultmapping.modifiers.model.FieldModifiersClass; import org.eclipse.yasson.defaultmapping.modifiers.model.MethodModifiersClass; import org.eclipse.yasson.defaultmapping.modifiers.model.PrivateConstructorClass; import org.eclipse.yasson.defaultmapping.modifiers.model.ProtectedConstructorClass; import jakarta.json.bind.JsonbException; /** * Test access modifiers for default mapping. * * @author Roman Grigoriadi */ public class DefaultMappingModifiersTest { @Test public void testFieldModifiers() { FieldModifiersClass fieldModifiersClass = new FieldModifiersClass(); assertEquals("{\"finalString\":\"FINAL_STRING\"}", defaultJsonb.toJson(fieldModifiersClass)); FieldModifiersClass result = defaultJsonb.fromJson("{\"finalString\":\"FINAL_STRING\",\"staticString\":\"STATIC_STRING\",\"transientString\":\"TRANSIENT_STRING\"}", FieldModifiersClass.class); //no setter throwing illegal has been called. } @Test public void testMethodModifiers() { MethodModifiersClass methodModifiers = new MethodModifiersClass(); methodModifiers.publicFieldWithoutMethods = "WITHOUT_METHODS"; String validJson = "{\"getterWithoutFieldValue\":\"GETTER_WITHOUT_FIELD\",\"publicFieldWithoutMethods\":\"WITHOUT_METHODS\"}"; assertEquals(validJson, defaultJsonb.toJson(methodModifiers)); MethodModifiersClass result = defaultJsonb.fromJson("{\"publicFieldWithPrivateMethods\":\"value\"}", MethodModifiersClass.class); assertNull(result.publicFieldWithPrivateMethods); result = defaultJsonb.fromJson(validJson, MethodModifiersClass.class); assertEquals("WITHOUT_METHODS", result.publicFieldWithoutMethods); } @Test public void testConstructorModifiers() { try{ ProtectedConstructorClass instance = defaultJsonb.fromJson("{\"randomField\":\"test\"}", ProtectedConstructorClass.class); assertEquals(instance.randomField, "test"); } catch (JsonbException e){ fail("No exception should be thrown for protected constructor"); throw e; } try { defaultJsonb.fromJson("{\"randomField\":\"test\"}", PrivateConstructorClass.class); fail("Exception should have been thrown"); }catch (JsonbException e){ assertTrue(e.getMessage().endsWith("Can't create instance")); } } @Test public void testMultipleInstancesOfSameType() { Person person = new Person(); Person personTwo = new Person(); person.name = "Person 1"; personTwo.name = "Person 2"; person.child = personTwo; assertEquals("{\"child\":{\"name\":\"Person 2\"},\"name\":\"Person 1\"}", bindingJsonb.toJson(person)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ChildOfPackagePrivateParent.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author David Kral */ public class ChildOfPackagePrivateParent extends PackagePrivateParent { public String name; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/FieldModifiersClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author Roman Grigoriadi */ public class FieldModifiersClass { private final String finalString = "FINAL_STRING"; private static final Long serialVersionUID = Long.MAX_VALUE; private static String staticString = "STATIC_STRING"; private transient String transientString = "TRANSIENT_STRING"; public String getFinalString() { return finalString; } public void setFinalString() { throw new IllegalStateException(); } public static Long getSerialVersionUID() { throw new IllegalStateException(); } public String getTransientString() { throw new IllegalStateException(); } public void setTransientString(String transientString) { throw new IllegalStateException(); } public static String getStaticString() { throw new IllegalStateException(); } public static void setStaticString(String staticString) { throw new IllegalStateException(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/MethodModifiersClass.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; import java.util.function.Consumer; /** * @author Roman Grigoriadi */ public class MethodModifiersClass { private Consumer setterWithoutFieldConsumer = s -> {}; public String publicFieldWithPrivateMethods; public String publicFieldWithoutMethods; public void setSetterWithoutFieldConsumer(Consumer setterWithoutFieldConsumer) { this.setterWithoutFieldConsumer = setterWithoutFieldConsumer; } public String getGetterWithoutFieldValue() { return "GETTER_WITHOUT_FIELD"; } public void setGetterWithoutFieldValue(String value) { setterWithoutFieldConsumer.accept(value); } private String getPublicFieldWithPrivateMethods() { return publicFieldWithPrivateMethods; } private void setPublicFieldWithPrivateMethods(String publicFieldWithPrivateMethods) { this.publicFieldWithPrivateMethods = publicFieldWithPrivateMethods; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PackagePrivateParent.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author David Kral */ class PackagePrivateParent { public int id; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/Person.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author David Kral */ public class Person { public String name; public Person child; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PrivateConstructorClass.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author David Kral */ public class PrivateConstructorClass { public String randomField; private PrivateConstructorClass(){ } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ProtectedConstructorClass.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.modifiers.model; /** * @author David Kral */ public class ProtectedConstructorClass { public String randomField; protected ProtectedConstructorClass(){ } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/properties/PropertiesTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.properties; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import java.io.IOException; import java.util.Locale; /** * This class contains properties tests * * @author David Kral */ public class PropertiesTest { @Test public void testPropertiesWithoutLocale() throws IOException { String template = "Process class: {0} from json using converter: {1}"; String message = Messages.getMessage(MessageKeys.PROCESS_FROM_JSON); assertEquals(template, message); } @Test public void testPropertiesWithLocale() throws IOException { String templateCS = "Zpracovávám třídu: {0} do jsonu za použití convertoru: {1}"; String messageCS = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("cs")); String templateEN = "Process class: {0} to json using converter: {1}"; String messageEN = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("en")); assertEquals(templateCS, messageCS); assertEquals(templateEN, messageEN); } @Test public void testPropertiesAttributeSetting() throws IOException { String template = "Zpracovávám třídu: Test do jsonu za použití convertoru: Test1"; String message = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("cs"), "Test", "Test1"); assertEquals(template, message); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/CustomerTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.defaultmapping.specific.model.Address; import org.eclipse.yasson.defaultmapping.specific.model.Customer; import org.eclipse.yasson.defaultmapping.specific.model.Street; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Roman Grigoriadi */ public abstract class CustomerTest { protected void assertCustomerValues(Customer customer, String customerName) { assertEquals(customerName, customer.getName()); assertEquals(Integer.valueOf(33), customer.getAge()); /*assertEquals(1, customer.getAddresses().size()); for (Address address : customer.getAddresses()) { assertEquals("Prague", address.getTown()); assertEquals("Zoubkova88", address.getStreet().getName()); assertEquals(Integer.valueOf(111), address.getStreet().getNumber()); }*/ if (customer.getStrings() != null) { assertEquals(2, customer.getStrings().size()); assertEquals("green", customer.getStrings().get(0)); assertEquals("yellow", customer.getStrings().get(1)); } if (customer.getIntegers() != null) { assertEquals(2, customer.getIntegers().size()); assertEquals(Integer.valueOf(0), customer.getIntegers().get(0)); assertEquals(Integer.valueOf(1), customer.getIntegers().get(1)); } /*assertEquals(2, customer.getStringIntegerMap().size()); assertEquals(Integer.valueOf(1), customer.getStringIntegerMap().get("first")); assertEquals(Integer.valueOf(2), customer.getStringIntegerMap().get("second"));*/ } protected static Customer createCustomer(String customerName) { Street street = new Street("Zoubkova", 111); Address address = new Address(street, "Prague"); Customer customer = new Customer(33, customerName); List
addresses = new ArrayList<>(); addresses.add(address); customer.setAddresses(addresses); List strings = new ArrayList<>(); strings.add("green"); strings.add("yellow"); customer.setStrings(strings); List integers = new ArrayList<>(); integers.add(0); integers.add(1); customer.setIntegers(integers); Map stringIntegerMap = new HashMap<>(); stringIntegerMap.put("first", 1); stringIntegerMap.put("second", 2); customer.setStringIntegerMap(stringIntegerMap); List> listOfListsOfIntegers = new ArrayList<>(); for(int i=0; i<3; i++) { List integerList = new ArrayList<>(); integerList.add(0); integerList.add(1); integerList.add(2); listOfListsOfIntegers.add(integerList); } customer.setListOfListsOfIntegers(listOfListsOfIntegers); return customer; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/JsonStreamsTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; /** * Tests calling JSONB with {@link java.util.stream.Stream} and {@link Readable} * * @author Roman Grigoriadi */ public class JsonStreamsTest { private static final String CHARSET = "UTF8"; @Test public void testUnmarshall() throws Exception { String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}"; Map result = defaultJsonb.fromJson(new InputStreamReader(new ByteArrayInputStream(json.getBytes(CHARSET)), Charset.forName(CHARSET)), new TestTypeToken>(){}.getType()); assertMapValues(result); result = defaultJsonb.fromJson(new ByteArrayInputStream(json.getBytes(CHARSET)), new TestTypeToken>() {}.getType()); assertMapValues(result); } @Test public void testMarshall() throws Exception { String expected = "{\"key1\":\"value1\",\"key2\":\"value2\"}"; int len = expected.getBytes(CHARSET).length; Map strMap = new HashMap<>(); strMap.put("key1", "value1"); strMap.put("key2", "value2"); ByteArrayOutputStream baos = new ByteArrayOutputStream(len); defaultJsonb.toJson(strMap, baos); assertEquals(expected, baos.toString(CHARSET)); baos = new ByteArrayOutputStream(len); OutputStreamWriter writer = new OutputStreamWriter(baos, Charset.forName(CHARSET)); defaultJsonb.toJson(strMap, writer); writer.close(); assertEquals(expected, baos.toString(CHARSET)); } private static void assertMapValues(Map result) { assertEquals(2, result.size()); assertEquals("value1", result.get("key1")); assertEquals("value2", result.get("key2")); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/NullTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import org.eclipse.yasson.defaultmapping.specific.model.StreetWithPrimitives; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.specific.model.Street; import java.util.List; import java.util.Map; /** * @author Roman Grigoriadi */ public class NullTest { @Test public void testSetsNullIntoFields() { String json = "{\"name\":null,\"number\":null}"; Street result = defaultJsonb.fromJson(json, Street.class); //these have default initialization value assertNull(result.getName()); assertNull(result.getNumber()); } // // @Test // public void testSetsNullToPrimitives() { // String json = "{\"name\":null,\"number\":null}"; // // StreetWithPrimitives result = defaultJsonb.fromJson(json, StreetWithPrimitives.class); // //these have default initialization value // assertNull(result.getName()); // assertNull(result.getNumber()); // } @Test public void testDeserializeNull() { assertNull(defaultJsonb.fromJson("null", Object.class)); } @Test public void testDeserializeNullPojo() { assertNull(defaultJsonb.fromJson("null", Street.class)); } @Test public void testDeserializeNullList() { assertNull(defaultJsonb.fromJson("null", new TestTypeToken>() {}.getType())); } @Test public void testDeserializeNullMap() { assertNull(defaultJsonb.fromJson("null", new TestTypeToken>() {}.getType())); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/ObjectGraphTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.defaultmapping.specific.model.Customer; import java.util.HashMap; import java.util.Map; /** * @author Roman Grigoriadi */ public class ObjectGraphTest extends CustomerTest { private static final String EXPECTED = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"friends\":{\"firstFriend\":{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Jasons first friend\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]},\"secondFriend\":{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Jasons second friend\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}},\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Root Jason Customer\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}"; @Test public void testObjectToJson() { Customer customer = createCustomer("Root Jason Customer"); Map friends = new HashMap<>(); friends.put("firstFriend", createCustomer("Jasons first friend")); friends.put("secondFriend", createCustomer("Jasons second friend")); customer.setFriends(friends); assertEquals(EXPECTED, bindingJsonb.toJson(customer)); } @Test public void testObjectFromJson() { Customer customer = bindingJsonb.fromJson(EXPECTED, Customer.class); assertCustomerValues(customer, "Root Jason Customer"); assertEquals(2, customer.getFriends().size()); assertCustomerValues(customer.getFriends().get("firstFriend"), "Jasons first friend"); assertCustomerValues(customer.getFriends().get("secondFriend"), "Jasons second friend"); } @Test public void testSimpleObject() { Person person = new Person(); person.name = "David"; person.surname = "Kral"; String json = bindingJsonb.toJson(person); Person deser = bindingJsonb.fromJson(json, Person.class); System.out.println(); } public static class Person { public String name; public String surname; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020 Payara Foundation and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import org.eclipse.yasson.defaultmapping.specific.model.SpecificOptionalWrapper; import org.junit.jupiter.api.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import org.eclipse.yasson.defaultmapping.specific.model.OptionalWrapper; import org.eclipse.yasson.defaultmapping.specific.model.NotMatchingGettersAndSetters; import org.eclipse.yasson.defaultmapping.specific.model.Street; import org.eclipse.yasson.internal.JsonBindingBuilder; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import java.util.*; /** * Default mapping Optional* tests. * * @author Dmitry Kornilov */ public class OptionalTest { @Test public void testOptionalString() { assertEquals("{\"value\":\"abc\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Optional.of("abc")))); ScalarValueWrapper result = bindingJsonb.fromJson("{\"value\":\"abc\"}", new TestTypeToken>() {}.getType()); assertEquals(Optional.of("abc"), result.getValue()); } @Test public void testOptionalObject() { final OptionalWrapper optionalWrapper = new OptionalWrapper(); Street street = new Street("Xaveriova", 110); optionalWrapper.setStreetOptional(Optional.of(street)); assertEquals("{\"streetOptional\":{\"name\":\"Xaveriova\",\"number\":110}}", bindingJsonb.toJson(optionalWrapper)); OptionalWrapper result = bindingJsonb.fromJson("{\"streetOptional\":{\"name\":\"Xaveriova\",\"number\":110}}", OptionalWrapper.class); assertTrue(result.getStreetOptional().isPresent()); assertEquals("Xaveriova", result.getStreetOptional().get().getName()); assertEquals(Integer.valueOf(110), result.getStreetOptional().get().getNumber()); } @Test public void testMarshallOptional() { assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalInt.empty()))); assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalLong.empty()))); assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalDouble.empty()))); assertEquals("{\"value\":10}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalInt.of(10)))); assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalLong.of(100L)))); assertEquals("{\"value\":10.0}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalDouble.of(10.0D)))); final ScalarValueWrapper result = bindingJsonb.fromJson("{\"value\":10}", new TestTypeToken>() {}.getType()); assertEquals(OptionalInt.of(10), result.getValue()); } @Test public void testMarshallOptionalObject() { assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(Optional.empty()))); assertEquals("{\"id\":1,\"name\":\"Cust1\"}", bindingJsonb.toJson(Optional.of(new Customer(1, "Cust1")))); } @Test public void testMarshallOptionalIntArray() { final OptionalInt[] array = {OptionalInt.of(1), OptionalInt.of(2), OptionalInt.empty()}; assertEquals("[1,2,null]", bindingJsonb.toJson(array)); OptionalInt[] result = bindingJsonb.fromJson("[1,2,null]", new TestTypeToken() {}.getType()); assertTrue(result[0].isPresent()); assertEquals(1, result[0].getAsInt()); assertTrue(result[1].isPresent()); assertEquals(2, result[1].getAsInt()); assertEquals(OptionalInt.empty(), result[2]); } @Test public void testMarshallOptionalArray() { final Optional[] array = {Optional.of(new Customer(1, "Cust1")), Optional.of(new Customer(2, "Cust2")), Optional.empty()}; assertEquals("[{\"id\":1,\"name\":\"Cust1\"},{\"id\":2,\"name\":\"Cust2\"},null]", bindingJsonb.toJson(array)); } @Test public void testUnmarshallNullAsOptionalEmpty() { final ScalarValueWrapper result = bindingJsonb.fromJson("{\"value\":null}", new ScalarValueWrapper() { }.getClass().getGenericSuperclass()); assertEquals(OptionalInt.empty(), result.getValue()); } @Test public void testUnmarshallOptionalArrayNulls() { final OptionalLong[] result = bindingJsonb.fromJson("[null, null]", OptionalLong[].class); assertEquals(2, result.length); for (OptionalLong item : result) { assertEquals(OptionalLong.empty(), item); } } @Test public void testUnmarshallOptionalList() { final List> result = bindingJsonb.fromJson("[null, null]", new TestTypeToken>>() {}.getType()); assertEquals(2, result.size()); for (Optional item : result) { assertEquals(Optional.empty(), item); } } @Test public void testMarshallOptionalMap() { Map ints = new HashMap<>(); ints.put("first", OptionalInt.empty()); ints.put("second", OptionalInt.empty()); String result = defaultJsonb.toJson(ints); assertEquals("{\"first\":null,\"second\":null}", result); } @Test public void testCorrectOptionalGetter() { NotMatchingGettersAndSetters personWithCorrectGetter = new NotMatchingGettersAndSetters(); String result = defaultJsonb.toJson(personWithCorrectGetter); assertEquals("{\"firstName\":1,\"lastName\":\"Correct\"}", result); NotMatchingGettersAndSetters deserialized = defaultJsonb.fromJson(result, NotMatchingGettersAndSetters.class); personWithCorrectGetter.setFirstName(1); assertEquals(personWithCorrectGetter, deserialized); } @Test public void testMarshalEmptyRoot() { assertEquals("null", bindingJsonb.toJson(Optional.empty())); } @Test public void testUnmarshalEmptyRoot() { assertEquals(Optional.empty(), bindingJsonb.fromJson("null", new TestTypeToken>() {}.getType())); } @Test public void testMarshalEmptyInt() { assertEquals("null", bindingJsonb.toJson(OptionalInt.empty())); } @Test public void testUnmarshalEmptyInt() { assertEquals(OptionalInt.empty(), bindingJsonb.fromJson("null", OptionalInt.class)); } @Test public void testMarshalEmptyLong() { assertEquals("null", bindingJsonb.toJson(OptionalLong.empty())); } @Test public void testUnmarshalEmptyLong() { assertEquals(OptionalLong.empty(), bindingJsonb.fromJson("null", OptionalLong.class)); } @Test public void testMarshalEmptyDouble() { assertEquals("null", bindingJsonb.toJson(OptionalDouble.empty())); } @Test public void testUnmarshalEmptyDouble() { assertEquals(OptionalDouble.empty(), bindingJsonb.fromJson("null", OptionalDouble.class)); } @Test public void testNullInsteadOfOptional() { OptionalWrapper optionalWrapper = new OptionalWrapper(); String expected = "{}"; assertThat(bindingJsonb.toJson(optionalWrapper), is(expected)); } @Test public void testNullInsteadOfOptionalInSpecificOptionals() { SpecificOptionalWrapper optionalWrapper = new SpecificOptionalWrapper(); String expected = "{}"; assertThat(bindingJsonb.toJson(optionalWrapper), is(expected)); } public static class Customer { private int id; private String name; public Customer() { } public Customer(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.Arrays; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.Jsonbs; import org.eclipse.yasson.adapters.model.Chain; import org.eclipse.yasson.adapters.model.ChainAdapter; import org.eclipse.yasson.adapters.model.ChainSerializer; import org.eclipse.yasson.adapters.model.Foo; import org.eclipse.yasson.adapters.model.FooAdapter; import org.eclipse.yasson.adapters.model.FooSerializer; import org.junit.jupiter.api.Test; public class RecursiveReferenceTest { private static final Jsonb userSerializerJsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new ChainSerializer(), new FooSerializer())); private static final Jsonb adapterSerializerJsonb = JsonbBuilder.create(new JsonbConfig() .withAdapters(new ChainAdapter(), new FooAdapter())); @Test public void testSerializeRecursiveReference() { Chain recursive = new Chain("test"); recursive.setLinksTo(recursive); try { Jsonbs.defaultJsonb.toJson(recursive); fail("Exception should be caught"); } catch (JsonbException e) { assertEquals( "Unable to serialize property 'linksTo' from org.eclipse.yasson.adapters.model.Chain", e.getMessage()); assertEquals( "Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.", e.getCause().getMessage()); } } @Test public void testSerializeRecursiveReferenceCustomAdapter() { Chain recursive = new Chain("test"); recursive.setLinksTo(recursive); try { adapterSerializerJsonb.toJson(recursive); fail("Exception should be caught"); } catch (JsonbException e) { assertEquals("Problem adapting object of type class org.eclipse.yasson.adapters.model.Chain to java.util.Map in class class org.eclipse.yasson.adapters.model.ChainAdapter", e.getMessage()); } } @Test public void testSerializeRecursiveReferenceCustomSerializer() { Chain recursive = new Chain("test"); recursive.setLinksTo(recursive); try { userSerializerJsonb.toJson(recursive); fail("Exception should be caught"); } catch (JsonbException e) { assertEquals("Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.", e.getMessage()); } } @Test public void testSerializeRepeatedInstance() { String noNulls = "[{\"linksTo\":{\"name\":\"test\"},\"name\":\"test\"},{\"linksTo\":{\"name\":\"test\"}," + "\"name\":\"test\"}]"; String withNulls = "[{\"has\":null,\"linksTo\":{\"has\":null,\"linksTo\":null,\"name\":\"test\"},\"name\":\"test\"}," + "{\"has\":null,\"linksTo\":{\"has\":null,\"linksTo\":null,\"name\":\"test\"},\"name\":\"test\"}]"; checkSerializeRepeatedInstance(Jsonbs.defaultJsonb, noNulls); //Since ChainAdapter is adapting Chain to Map, the produced json will contain nulls checkSerializeRepeatedInstance(adapterSerializerJsonb, withNulls); checkSerializeRepeatedInstance(userSerializerJsonb, noNulls); } private void checkSerializeRepeatedInstance(Jsonb jsonb, String expected) { Chain recursive = new Chain("test"); recursive.setLinksTo(new Chain("test")); String result = jsonb.toJson(Arrays.asList(recursive, recursive)); assertEquals(expected, result); } @Test public void testSerialize2ReferencesSameObject() { A a = new A(); Foo b = new Foo("foo"); a.ref1 = b; a.ref2 = b; String result = Jsonbs.defaultJsonb.toJson(a); assertEquals("{\"ref1\":{\"bar\":\"foo\"},\"ref2\":{\"bar\":\"foo\"}}", result); } @Test public void testChain() { String noNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"name\":\"c2\"},\"name\":\"c1\"}"; String withNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":null," + "\"name\":\"c2\"},\"name\":\"c1\"}"; checkChain(Jsonbs.defaultJsonb, noNulls); //Since ChainAdapter is adapting Chain to Map, the produced json will contain nulls checkChain(adapterSerializerJsonb, withNulls); checkChain(userSerializerJsonb, noNulls); } private void checkChain(Jsonb jsonb, String expected) { Foo foo = new Foo("foo"); Chain c1 = new Chain("c1"); Chain c2 = new Chain("c2"); c1.setLinksTo(c2); c1.setHas(foo); c2.setHas(foo); String result = jsonb.toJson(c1); assertEquals(expected, result); } @Test public void testDeeperChain() { String noNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"name\":\"c3\"}," + "\"name\":\"c2\"},\"name\":\"c1\"}"; String withNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":null," + "\"linksTo\":null,\"name\":\"c3\"},\"name\":\"c2\"},\"name\":\"c1\"}"; checkDeeperChain(Jsonbs.defaultJsonb, noNulls); //Since ChainAdapter is adapting Chain to Map, the produced json will contain nulls checkDeeperChain(adapterSerializerJsonb, withNulls); checkDeeperChain(userSerializerJsonb, noNulls); } private void checkDeeperChain(Jsonb jsonb, String expected) { Foo foo = new Foo("foo"); Chain c1 = new Chain("c1"); Chain c2 = new Chain("c2"); Chain c3 = new Chain("c3"); c1.setLinksTo(c2); c1.setHas(foo); c2.setHas(foo); c2.setLinksTo(c3); String result = jsonb.toJson(c1); assertEquals(expected, result); } public static class A { public Foo ref1; public Foo ref2; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/SpecificTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; /** * Specific standard Java SE types tests: {@link BigDecimal}, {@link BigInteger}, {@link URL}, {@link URI}. * * @author Dmitry Kornilov */ public class SpecificTest { @Test public void testMarshallBigDecimal() { assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigDecimal.valueOf(100L)))); assertEquals("{\"value\":100.1}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigDecimal.valueOf(100.1D)))); } @Test public void testMarshallBigInteger() { assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigInteger.valueOf(100)))); } @Test public void testMarshallUri() throws URISyntaxException { assertEquals("{\"value\":\"http://www.oracle.com\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(new URI("http://www.oracle.com")))); } @Test public void testMarshallUrl() throws MalformedURLException { assertEquals("{\"value\":\"http://www.oracle.com\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(new URL("http://www.oracle.com")))); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass; import org.eclipse.yasson.defaultmapping.specific.model.ClassWithUnsupportedFields; import org.eclipse.yasson.defaultmapping.specific.model.CustomUnsupportedInterface; import org.eclipse.yasson.defaultmapping.specific.model.SupportedTypes; import org.eclipse.yasson.defaultmapping.specific.model.SupportedTypes.NestedPojo; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.eclipse.yasson.YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * @author Roman Grigoriadi */ public class UnmarshallingUnsupportedTypesTest { @Test public void testUnmarshallToUnsupportedInterface() { ClassWithUnsupportedFields unsupported = new ClassWithUnsupportedFields(); unsupported.customInterface = new CustomUnsupportedInterface() { private String value = "value1"; @Override public String getValue() { return value; } @Override public void setValue(String value) { throw new IllegalStateException("Not supposed to be called."); } }; String expected = "{\"customInterface\":{\"value\":\"value1\"}}"; assertEquals(expected, defaultJsonb.toJson(unsupported)); try { defaultJsonb.fromJson(expected, ClassWithUnsupportedFields.class); fail("Should report an error"); } catch (JsonbException e) { assertTrue(e.getMessage().contains("Cannot infer a type")); assertTrue(e.getMessage().contains("customInterface")); } } @Test public void testPojoForMalformedJson() { SupportedTypes supportedTypes = new SupportedTypes(); LocalDateTime localDateTime = LocalDateTime.of(2015, 12, 28, 15, 57); ZoneId prague = ZoneId.of("Europe/Prague"); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, prague); supportedTypes.setInstant(Instant.from(zonedDateTime)); supportedTypes.setOptionalLong(OptionalLong.of(11L)); supportedTypes.setZonedDateTime(zonedDateTime); supportedTypes.setNestedPojo(new SupportedTypes.NestedPojo()); supportedTypes.getNestedPojo().setIntegerValue(10); String json = "{\"instant\":\"2015-12-28T14:57:00Z\",\"nestedPojo\":{\"integerValue\":10},\"optionalLong\":11,\"zonedDateTime\":\"2015-12-28T15:57:00+01:00[Europe/Prague]\"}"; assertEquals(json, defaultJsonb.toJson(supportedTypes)); SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertEquals(result.getInstant(), supportedTypes.getInstant()); assertEquals(result.getZonedDateTime(), supportedTypes.getZonedDateTime()); assertEquals(result.getOptionalLong(), supportedTypes.getOptionalLong()); assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue()); } @Test public void testPojoAsScalarValue() { //wrong, nestedPojo is a value. String json = "{\"nestedPojo\":\"10\",\"optionalLong\":11}"; assertFail(json, SupportedTypes.class, "nestedPojo", NestedPojo.class); } @Test public void testPojoAsArray() { //wrong, nestedPojo is a collection. String json = "{\"nestedPojo\":[\"10\"],\"optionalLong\":11}"; assertFail(json, SupportedTypes.class, "nestedPojo", NestedPojo.class); } @Test() public void testMissingFieldDefault() { String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue()); assertEquals(11, result.getOptionalLong().getAsLong()); } @Test() public void testMissingFieldDefaultNull() { String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":null},\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue()); assertEquals(11, result.getOptionalLong().getAsLong()); } @Test public void testMissingFieldIgnored() { assertThrows(JsonbException.class, () -> { Jsonb defaultConfig = JsonbBuilder.create(new JsonbConfig().setProperty(FAIL_ON_UNKNOWN_PROPERTIES, true)); String json = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}"; SupportedTypes result = defaultConfig.fromJson(json, SupportedTypes.class); }); } @Test public void testMissingFieldWithObjectValue() { String json = "{\"missingProperty\":{\"optionalLong\":404},\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertTrue(result.getOptionalLong().isPresent()); assertThat(result.getOptionalLong().getAsLong(), is(11L)); } @Test public void testMissingFieldWithArrayValue() { String json = "{\"missingProperty\":[404],\"optionalLong\":11}"; SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class); assertTrue(result.getOptionalLong().isPresent()); assertThat(result.getOptionalLong().getAsLong(), is(11L)); } @Test public void testEmptyStringAsInteger() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Integer.class); } @Test public void testEmptyStringAsDouble() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Double.class); } @Test public void testEmptyStringAsFloat() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Float.class); } @Test public void testEmptyStringAsLong() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Long.class); } @Test public void testEmptyStringAsShort() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Short.class); } @Test public void testEmptyStringAsByte() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Byte.class); } @Test public void testEmptyStringAsBigDecimal() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type,"field1", BigDecimal.class); } @Test public void testEmptyStringAsBigInteger() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", BigInteger.class); } @Test public void testEmptyStringAsOptionalDouble() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type,"field1", Double.class); //We are reusing Double deserializer } @Test public void testEmptyStringAsOptionalInt() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type, "field1", Integer.class); //We are reusing Integer deserializer } @Test public void testEmptyStringAsOptionalLong() { Type type = new TestTypeToken>(){}.getType(); assertFail("{\"field1\":\"\"}", type,"field1", Long.class); //We are reusing Long deserializer } private void assertFail(String json, Type type, String failureProperty, Class failurePropertyClass) { try { defaultJsonb.fromJson(json, type); fail(); } catch (JsonbException e) { if(!e.getMessage().contains(failureProperty) || !e.getMessage().contains(failurePropertyClass.getName())) { fail("Expected error message to contain '" + failureProperty + "' and '" + failurePropertyClass.getName() + "', but was: " + e.getMessage()); } } } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Address.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; /** * @author Roman Grigoriadi */ public class Address { private Street street; private String town; public Address() { } public Address(Street street, String town) { this.street = street; this.town = town; } public Street getStreet() { return street; } public void setStreet(Street street) { this.street = street; } public String getTown() { return town; } public void setTown(String town) { this.town = town; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/ClassWithUnsupportedFields.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; /** * @author Roman Grigoriadi */ public class ClassWithUnsupportedFields { public CustomUnsupportedInterface customInterface; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/CustomUnsupportedInterface.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; /** * Unmarhsalling to this interface is not supported. * * @author Roman Grigoriadi */ public interface CustomUnsupportedInterface { String getValue(); void setValue(String value); } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Customer.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; import java.util.List; import java.util.Map; /** * Just a dummy pojo object, for testing object tree encapsulation. * Not much business sense here to look for. * * @author Roman Grigoriadi */ public class Customer { private List
addresses; private List strings; private List integers; private List> listOfListsOfIntegers; private Map stringIntegerMap; private Map friends; private Integer age; private String name; public Customer() { } public Customer(Integer age, String name) { this.age = age; this.name = name; } public List
getAddresses() { return addresses; } public void setAddresses(List
addresses) { this.addresses = addresses; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getStrings() { return strings; } public void setStrings(List strings) { this.strings = strings; } public List getIntegers() { return integers; } public void setIntegers(List integers) { this.integers = integers; } public Map getStringIntegerMap() { return stringIntegerMap; } public void setStringIntegerMap(Map stringIntegerMap) { this.stringIntegerMap = stringIntegerMap; } public Map getFriends() { return friends; } public void setFriends(Map friends) { this.friends = friends; } public List> getListOfListsOfIntegers() { return listOfListsOfIntegers; } public void setListOfListsOfIntegers(List> listOfListsOfIntegers) { this.listOfListsOfIntegers = listOfListsOfIntegers; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/NotMatchingGettersAndSetters.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; import java.util.Objects; import java.util.Optional; /** * @author David Kral. */ public class NotMatchingGettersAndSetters { private String firstName = "Person"; private String lastName = "Correct"; public void setFirstName(Integer firstName) { this.firstName = Integer.toString(firstName); } public Optional getFirstName() { return Optional.of(1); } public void setLastName(String lastName) { this.lastName = lastName; } public Optional getLastName() { return Optional.ofNullable(lastName); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotMatchingGettersAndSetters that = (NotMatchingGettersAndSetters) o; return Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName); } @Override public int hashCode() { return Objects.hash(firstName, lastName); } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/OptionalWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; import java.util.Optional; /** * @author Roman Grigoriadi */ public class OptionalWrapper { private Optional streetOptional; public Optional getStreetOptional() { return streetOptional; } public void setStreetOptional(Optional streetOptional) { this.streetOptional = streetOptional; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SpecificOptionalWrapper.java ================================================ /* * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; public class SpecificOptionalWrapper { private OptionalInt optionalInt; private OptionalLong optionalLong; private OptionalDouble optionalDouble; public OptionalInt optionalInt() { return optionalInt; } public void setOptionalInt(OptionalInt optionalInt) { this.optionalInt = optionalInt; } public OptionalLong optionalLong() { return optionalLong; } public void setOptionalLong(OptionalLong optionalLong) { this.optionalLong = optionalLong; } public OptionalDouble optionalDouble() { return optionalDouble; } public void setOptionalDouble(OptionalDouble optionalDouble) { this.optionalDouble = optionalDouble; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Street.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; /** * @author Roman Grigoriadi */ public class Street { private String name = "defaultName"; private Integer number = 11; public Street() { } public Street(String name, Integer number) { this.name = name; this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/StreetWithPrimitives.java ================================================ /* * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; /** * @author Roman Grigoriadi */ public class StreetWithPrimitives { private String name = "defaultName"; private int number = 11; public StreetWithPrimitives() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SupportedTypes.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.specific.model; import java.time.Instant; import java.time.ZonedDateTime; import java.util.OptionalLong; /** * @author Roman Grigoriadi */ public class SupportedTypes { public static class NestedPojo { private Integer integerValue; public Integer getIntegerValue() { return integerValue; } public void setIntegerValue(Integer integerValue) { this.integerValue = integerValue; } } private Instant instant; private ZonedDateTime zonedDateTime; private OptionalLong optionalLong; private NestedPojo nestedPojo; public Instant getInstant() { return instant; } public void setInstant(Instant instant) { this.instant = instant; } public OptionalLong getOptionalLong() { return optionalLong; } public void setOptionalLong(OptionalLong optionalLong) { this.optionalLong = optionalLong; } public ZonedDateTime getZonedDateTime() { return zonedDateTime; } public void setZonedDateTime(ZonedDateTime zonedDateTime) { this.zonedDateTime = zonedDateTime; } public NestedPojo getNestedPojo() { return nestedPojo; } public void setNestedPojo(NestedPojo nestedPojo) { this.nestedPojo = nestedPojo; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/DefaultSerializersTest.java ================================================ /* * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper; import org.eclipse.yasson.defaultmapping.typeConvertors.model.ByteArrayWrapper; import org.eclipse.yasson.internal.JsonBindingBuilder; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.BinaryDataStrategy; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Base64; import java.util.UUID; /** * This class contains Converter tests * * @author David Kral */ public class DefaultSerializersTest { @Test public void testCharacter() { final String json = "{\"value\":\"\uFFFF\"}"; assertEquals(json, defaultJsonb.toJson(new ScalarValueWrapper<>('\uFFFF'))); ScalarValueWrapper result = defaultJsonb.fromJson(json, new TestTypeToken>(){}.getType()); assertEquals((Character)'\uFFFF', result.getValue()); } @Test public void testByteArray() { byte[] array = {1, 2, 3}; assertEquals("[1,2,3]", bindingJsonb.toJson(array)); } @Test public void testByteArrayWithBinaryStrategy() { byte[] array = {127, -128, 127}; Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build(); assertEquals("[127,-128,127]", jsonb.toJson(array)); assertArrayEquals(array, jsonb.fromJson("[127,-128,127]", byte[].class)); } @Test public void testByteArrayWithStrictJson() { byte[] array = {1, 2, 3}; ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(); byteArrayWrapper.array = array; Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true))).build(); assertEquals("{\"array\":\"" + Base64.getUrlEncoder().encodeToString(array) + "\"}", jsonb.toJson(byteArrayWrapper)); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(false))).build(); assertEquals("{\"array\":[1,2,3]}", jsonb.toJson(byteArrayWrapper)); } @Test public void testByteArrayWithStrictJsonAndBinaryStrategy() { byte[] array = {1, 2, 3}; ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(); byteArrayWrapper.array = array; Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build(); final String base64UrlEncodedJson = "{\"array\":\"" + Base64.getUrlEncoder().encodeToString(array) + "\"}"; assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper)); ByteArrayWrapper result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class); assertArrayEquals(array, result.array); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BASE_64))).build(); assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper)); result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class); assertArrayEquals(array, result.array); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BASE_64_URL))).build(); assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper)); result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class); assertArrayEquals(array, result.array); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build(); assertEquals("[1,2,3]", jsonb.toJson(array)); result = jsonb.fromJson("{\"array\":[1,2,3]}", ByteArrayWrapper.class); assertArrayEquals(array, result.array); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BASE_64))).build(); final String base64EncodedJson = "{\"array\":\"" + Base64.getEncoder().encodeToString(array) + "\"}"; assertEquals(base64EncodedJson, jsonb.toJson(byteArrayWrapper)); result = jsonb.fromJson(base64EncodedJson, ByteArrayWrapper.class); assertArrayEquals(array, result.array); jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BASE_64_URL))).build(); assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper)); result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class); assertArrayEquals(array, result.array); } @Test public void testUUID() { UUID uuid = UUID.randomUUID(); String json = defaultJsonb.toJson(uuid); UUID result = defaultJsonb.fromJson(json, UUID.class); assertEquals(uuid, result); } @Test public void serializeObjectWithPth() { Path expectedPath = Paths.get("/tmp/hello/me.txt"); String expectedPathString = expectedPath.toString().replace("\\", "\\\\"); String expectedJson = "{\"path\":\"" + expectedPathString + "\"}"; final ObjectWithPath objectWithPath = new ObjectWithPath(); objectWithPath.path = expectedPath; final String s = defaultJsonb.toJson(objectWithPath); assertEquals(expectedJson, s); ObjectWithPath actualObject = defaultJsonb.fromJson(expectedJson, ObjectWithPath.class); assertEquals(expectedPath, actualObject.path); } public static class ObjectWithPath { public Path path; } } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigDecimalWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors.model; import java.math.BigDecimal; /** * Created by David Kral. */ public class BigDecimalWrapper { public BigDecimal bigDecimal; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigIntegerWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors.model; import java.math.BigInteger; /** * Created by David Kral. */ public class BigIntegerWrapper { public BigInteger bigInteger; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/ByteArrayWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors.model; /** * Created by David Kral. */ public class ByteArrayWrapper { public byte[] array; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/CalendarWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors.model; import java.util.Calendar; /** * Created by David Kral. */ public class CalendarWrapper { public Calendar calendar; } ================================================ FILE: src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/StringWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.defaultmapping.typeConvertors.model; /** * Created by David Kral. */ public class StringWrapper { public String string; } ================================================ FILE: src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.documented; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.adapter.JsonbAdapter; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbNillable; import jakarta.json.bind.annotation.JsonbNumberFormat; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTransient; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; /** * Contains tests from http://json-b.net/docs/user-guide.html */ public class DocumentationExampleTest { public static class Dog { public String name; public int age; public boolean bitable; } @Test public void testMappingExample() { // Create a dog instance Dog dog = new Dog(); dog.name = "Falco"; dog.age = 4; dog.bitable = false; // Create Jsonb and serialize String result = defaultJsonb.toJson(dog); assertEquals("{\"age\":4,\"bitable\":false,\"name\":\"Falco\"}", result); // Deserialize back dog = defaultJsonb.fromJson("{\"name\":\"Falco\",\"age\":4,\"bites\":false}", Dog.class); assertEquals("Falco", dog.name); assertEquals(4, dog.age); assertEquals(false, dog.bitable); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void testMappingCollection() { Dog falco = new Dog(); falco.name = "Falco"; falco.age = 4; Dog cassidy = new Dog(); cassidy.name = "Cassidy"; cassidy.age = 5; // List of dogs List dogs = new ArrayList(); dogs.add(falco); dogs.add(cassidy); // Create Jsonb and serialize String result = defaultJsonb.toJson(dogs); assertEquals( "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", result); // We can also deserialize back into a raw collection, but since there is no way // to infer a type here, // the result will be a list of java.util.Map instances with string keys. dogs = defaultJsonb.fromJson(result, ArrayList.class); assertEquals(2, dogs.size()); assertEquals("Falco", ((Map) dogs.get(0)).get("name")); assertEquals("Cassidy", ((Map) dogs.get(1)).get("name")); // assertEquals(4, ((Map) dogs.get(0)).get("age")); // TODO should these // actually be BigDecimals? // assertEquals(5, ((Map) dogs.get(1)).get("age")); } @SuppressWarnings("serial") @Test public void testMappingGenericCollection() { Dog falco = new Dog(); falco.name = "Falco"; falco.age = 4; Dog cassidy = new Dog(); cassidy.name = "Cassidy"; cassidy.age = 5; // List of dogs List dogs = new ArrayList<>(); dogs.add(falco); dogs.add(cassidy); // Create Jsonb and serialize String result = defaultJsonb.toJson(dogs); assertEquals( "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]", result); // Deserialize back dogs = defaultJsonb.fromJson(result, new ArrayList() { }.getClass().getGenericSuperclass()); assertEquals(2, dogs.size()); assertEquals("Falco", dogs.get(0).name); assertEquals("Cassidy", dogs.get(1).name); } @Test public void testFormattedOutput() { Dog pojo = new Dog(); pojo.name = "Falco"; pojo.age = 4; // Use it! String result = formattingJsonb.toJson(pojo); assertEquals("{\n" + " \"age\": 4,\n" + " \"bitable\": false,\n" + " \"name\": \"Falco\"\n" + "}", result); } public static class Person1 { @JsonbProperty("person-name") public String name; public String profession; } @Test public void testChangingPropertyNames1() { Person1 p = new Person1(); p.name = "Jason Bourne"; p.profession = "Super Agent"; String result = formattingJsonb.toJson(p); assertEquals("{\n" + " \"person-name\": \"Jason Bourne\",\n" + " \"profession\": \"Super Agent\"\n" + "}", result); } public class Person2 { private String name; private String profession; @JsonbProperty("person-name") public String getName() { return name; } public String getProfession() { return profession; } // public setters ... public void setName(String name) { this.name = name; } public void setProfession(String profession) { this.profession = profession; } } @Test public void testChangingPropertyNames2() { Person2 p = new Person2(); p.name = "Jason Bourne"; p.profession = "Super Agent"; String result = formattingJsonb.toJson(p); assertEquals("{\n" + " \"person-name\": \"Jason Bourne\",\n" + " \"profession\": \"Super Agent\"\n" + "}", result); } public static class Person3 { private String name; @JsonbProperty("name-to-write") public String getName() { return name; } @JsonbProperty("name-to-read") public void setName(String name) { this.name = name; } } @Test public void testChangingPropertyNames3() { Person3 p = new Person3(); p.name = "Jason Bourne"; String result = defaultJsonb.toJson(p); assertEquals("{\"name-to-write\":\"Jason Bourne\"}", result); String json = "{\"name-to-read\":\"Jason Bourne\"}"; Person3 after = defaultJsonb.fromJson(json, Person3.class); assertEquals("Jason Bourne", after.name); } public static class Person4 { // TODO: a non-static class results in an NPE @JsonbTransient private String name; private String profession; // public getters/setters ... public String getName() { return name; } public void setName(String name) { this.name = name; } public String getProfession() { return this.profession; } public void setProfession(String profession) { this.profession = profession; } } @Test public void testIgnoringProperties() { Person4 p = new Person4(); p.name = "Jason Bourne"; p.profession = "Super Agent"; String result = defaultJsonb.toJson(p); assertEquals("{\"profession\":\"Super Agent\"}", result); String json = "{\"profession\":\"Super Agent\"}"; Person4 after = defaultJsonb.fromJson(json, Person4.class); assertEquals("Super Agent", after.profession); assertNull(after.name); } @JsonbNillable public class Person5 { private String name; private String profession; // public getters/setters ... public String getName() { return name; } public void setName(String name) { this.name = name; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } } @Test public void testNullHandling1() { Person5 p = new Person5(); String result = defaultJsonb.toJson(p); assertEquals("{\"name\":null,\"profession\":null}", result); } public class Person6 { @JsonbProperty(nillable=true) private String name; private String profession; // public getters/setters ... public String getName() { return name; } public void setName(String name) { this.name = name; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } } @Test public void testNullHandling2() { Person6 p = new Person6(); String result = defaultJsonb.toJson(p); assertEquals("{\"name\":null}", result); } public static class Person { public String name; public String profession; } @Test public void testNullHandling3() { Person p = new Person(); String result = nullableJsonb.toJson(p); assertEquals("{\"name\":null,\"profession\":null}", result); } public static class Person8 { // TODO: obscure error here if non-static public final String name; public String profession; @JsonbCreator public Person8(@JsonbProperty("name") String name) { this.name = name; } } @Test public void testCustomInstantiation() { Person8 p = defaultJsonb.fromJson("{\"name\":\"Jason Bourne\"}", Person8.class); assertEquals("Jason Bourne", p.name); } public static class Person9 { public String name; @JsonbDateFormat("dd.MM.yyyy") public LocalDate birthDate; @JsonbNumberFormat("#0.00") public BigDecimal salary; } @Test public void testDateNumberFormats1() { Person9 p = new Person9(); p.name = "Jason Bourne"; p.birthDate = LocalDate.of(1999, 8, 7); p.salary = new BigDecimal("123.45678"); String json = defaultJsonb.toJson(p); assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", json); Person9 after = defaultJsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", Person9.class); assertEquals(p.name, after.name); assertEquals(p.birthDate, after.birthDate); assertEquals(new BigDecimal("123.46"), after.salary); } public static class Person10 { public String name; public LocalDate birthDate; public BigDecimal salary; } @Test public void testDateNumberFormats2() { Person10 p = new Person10(); p.name = "Jason Bourne"; p.birthDate = LocalDate.of(1999, 8, 7); p.salary = new BigDecimal("123.45678"); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()// .withDateFormat("dd.MM.yyyy", null)); // TODO: why no withNumberFormat? String json = jsonb.toJson(p); assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", json); Person9 after = jsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", Person9.class); assertEquals(p.name, after.name); assertEquals(p.birthDate, after.birthDate); assertEquals(p.salary, after.salary); } public static class Customer { private int id; private String name; private String organization; private String position; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getOrganization() { return organization; } public void setOrganization(String organization) { this.organization = organization; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } } public static class CustomerAnnotated { @JsonbProperty("customer_id") private int id; @JsonbProperty("customer_name") private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public static class CustomerAdapter implements JsonbAdapter { @Override public CustomerAnnotated adaptToJson(Customer c) throws Exception { CustomerAnnotated customer = new CustomerAnnotated(); customer.setId(c.getId()); customer.setName(c.getName()); return customer; } @Override public Customer adaptFromJson(CustomerAnnotated adapted) throws Exception { Customer customer = new Customer(); customer.setId(adapted.getId()); customer.setName(adapted.getName()); return customer; } } @Test public void testAdapters1() { // Create customer Customer customer = new Customer(); customer.setId(1); customer.setName("Jason Bourne"); customer.setOrganization("Super Agents"); customer.setPosition("Super Agent"); // Serialize String json = defaultJsonb.toJson(customer); assertEquals("{\"id\":1,\"name\":\"Jason Bourne\",\"organization\":\"Super Agents\",\"position\":\"Super Agent\"}", json); } @Test public void testAdapters2() { // Create custom configuration JsonbConfig config = new JsonbConfig() .withAdapters(new CustomerAdapter()); // Create Jsonb with custom configuration Jsonb jsonb = JsonbBuilder.create(config); // Create customer Customer customer = new Customer(); customer.setId(1); customer.setName("Jason Bourne"); customer.setOrganization("Super Agents"); customer.setPosition("Super Agent"); // Serialize String json = jsonb.toJson(customer); assertEquals("{\"customer_id\":1,\"customer_name\":\"Jason Bourne\"}", json); } public static class CustomerSerializer implements JsonbSerializer { @Override public void serialize(Customer customer, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("customer_id", customer.getId()); generator.write("customer_name", customer.getName()); generator.writeEnd(); } } public static class CustomerDeserializer implements JsonbDeserializer { @Override public Customer deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { Customer customer = new Customer(); JsonParser.Event next; // Moving parser by hand looking for customer_id and customer_name properties while ((next = parser.next()) != JsonParser.Event.END_OBJECT) { if (next == JsonParser.Event.KEY_NAME) { String jsonKeyName = parser.getString(); // Move to json value parser.next(); if ("customer_id".equals(jsonKeyName)) { customer.setId(parser.getInt()); } else if ("customer_name".equals(jsonKeyName)) { customer.setName(parser.getString()); } } } return customer; } } @Test public void testSerializerDeserializer() { // Create pojo Customer customer = new Customer(); customer.setId(1); customer.setName("Freddie"); customer.setOrganization("Super Agents"); customer.setPosition("Super Agent"); // Also configurable with @JsonbSerializer / JsonbDeserializer on properties and class. JsonbConfig config = new JsonbConfig() .withSerializers(new CustomerSerializer()) .withDeserializers(new CustomerDeserializer()); Jsonb jsonb = JsonbBuilder.create(config); String json = jsonb.toJson(customer); assertEquals("{\"customer_id\":1,\"customer_name\":\"Freddie\"}", json); Customer result = jsonb.fromJson(json, Customer.class); assertEquals(customer.getId(), result.getId()); assertEquals(customer.getName(), result.getName()); assertNull(result.getOrganization()); assertNull(result.getPosition()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationFinderTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.TESTVALUE; import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.getConstructorAnnotationsOf; import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.getMethodAnnotationsOf; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.AnnotationAnnotatedWithDeprecated; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.AnnotationWithoutValueProperty; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithConstructAnnotation; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithDeprecatedAndIgnoredMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithDeprecatedMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithIgnoredAndInheritedDeprecatedMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithIgnoredMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithInheritedAndDirectlyDeprecatedMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithInheritedDeprecatedMethod; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithMissingValuePropertyAnnotation; import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithNoAnnotations; public class AnnotationFinderTest { /** * class under test. */ private AnnotationFinder findAnnotation = AnnotationFinder.findAnnotation(Deprecated.class); @Test public void testAnnotationShouldBeFound() { assertNotNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithDeprecatedMethod.class))); } @Test public void testAnnotationShouldBeFoundWithinOthers() { assertNotNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithDeprecatedAndIgnoredMethod.class))); } @Test public void testInheritedAnnotationShouldBeFound() { Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class)); assertEquals("inherited", annotation.since()); } @Test public void testInheritedAnnotationShouldBeFoundWithinOthers() { Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredAndInheritedDeprecatedMethod.class)); assertEquals("inherited", annotation.since()); } @Test public void testDirectAnnotationShouldBePreferedOverInheritedOnes() { Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithInheritedAndDirectlyDeprecatedMethod.class)); assertEquals("", annotation.since()); } @Test public void testResultShouldBeNullWhenThereIsNoAnnotation() { assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithNoAnnotations.class))); } @Test public void testResultShouldBeNullWhenThereAreOnlyOtherAnnotations() { assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredMethod.class))); } @Test @SuppressWarnings("deprecation") public void testValueOfAnnotationShouldBeExtractedDynamically() { findAnnotation = AnnotationFinder.findAnnotation(AnnotationAnnotatedWithDeprecated.class); assertEquals(TESTVALUE, findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class))); } @Test public void testValueOfAnnotationShouldBeNullIfAnnotationDoesNotExist() { findAnnotation = AnnotationFinder.findAnnotation(ObjectWithNoAnnotations.class); assertNull(findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class))); } @Test public void testValueOfConstructorAnnotation() { findAnnotation = AnnotationFinder.findConstructorProperties(); assertArrayEquals(new Object[] { TESTVALUE }, (Object[]) findAnnotation.valueIn(getConstructorAnnotationsOf(ObjectWithConstructAnnotation.class))); } @Test @SuppressWarnings("deprecation") public void testValueOfConstructorMetaAnnotation() { findAnnotation = AnnotationFinder.findAnnotation(AnnotationAnnotatedWithDeprecated.class); assertEquals(TESTVALUE, findAnnotation.valueIn(getConstructorAnnotationsOf(ObjectWithConstructAnnotation.class))); } @Test public void testIgnorenNonExistingOptionalAnnotationClassName() { findAnnotation = AnnotationFinder.findAnnotationByName("java.nnnooootttt.eexxxxiissttting.Class"); assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredMethod.class))); } @Test public void testIgnorenAnnotationAnnotationWithoutValueProperty() { findAnnotation = AnnotationFinder.findAnnotation(AnnotationWithoutValueProperty.class); assertNull(findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithMissingValuePropertyAnnotation.class))); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationFinderTestFixtures.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import java.beans.ConstructorProperties; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Disabled class AnnotationFinderTestFixtures { public static final String TESTVALUE = "testvalue"; public static final Annotation[] getMethodAnnotationsOf(Class clazz) { try { return clazz.getMethod("annotatedMethod").getAnnotations(); } catch (NoSuchMethodException | SecurityException e) { throw new IllegalStateException(e); } } public static final Annotation[] getConstructorAnnotationsOf(Class clazz) { try { return clazz.getConstructor(String.class).getAnnotations(); } catch (NoSuchMethodException | SecurityException e) { throw new IllegalStateException(e); } } public static class ObjectWithNoAnnotations { public void annotatedMethod() { // empty } } public static class ObjectWithDeprecatedMethod { @Deprecated public void annotatedMethod() { // empty } } public static class ObjectWithIgnoredMethod { @Disabled public void annotatedMethod() { // empty } } public static class ObjectWithDeprecatedAndIgnoredMethod { @Disabled @Deprecated public void annotatedMethod() { // empty } } public static class ObjectWithInheritedDeprecatedMethod { @AnnotationAnnotatedWithDeprecated() public void annotatedMethod() { // empty } } public static class ObjectWithIgnoredAndInheritedDeprecatedMethod { @Disabled @AnnotationAnnotatedWithDeprecated public void annotatedMethod() { // empty } } public static class ObjectWithInheritedAndDirectlyDeprecatedMethod { @Deprecated @AnnotationAnnotatedWithDeprecated public void annotatedMethod() { // empty } } public static class ObjectWithConstructAnnotation { @AnnotationAnnotatedWithDeprecated @ConstructorProperties({ TESTVALUE }) public ObjectWithConstructAnnotation(String testvalue) { // empty } } public static class ObjectWithMissingValuePropertyAnnotation { @AnnotationWithoutValueProperty public void annotatedMethod() { // empty } } @Retention(RetentionPolicy.RUNTIME) @Target(value = { METHOD, CONSTRUCTOR, TYPE }) @Deprecated(since = "inherited") public @interface AnnotationAnnotatedWithDeprecated { String value() default TESTVALUE; } @Retention(RetentionPolicy.RUNTIME) @Target(value = { METHOD, CONSTRUCTOR, TYPE }) public @interface AnnotationWithoutValueProperty { String someOtherProperty() default TESTVALUE; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTest.java ================================================ /* * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import jakarta.json.bind.config.PropertyNamingStrategy; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedFactoryMethod; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedProtectedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithTwoJsonbCreatorAnnotatedSpots; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor; import org.eclipse.yasson.internal.model.JsonbCreator; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.spi.JsonProvider; /** * Tests the {@link AnnotationIntrospector}. * * @see AnnotationIntrospectorTestFixtures * @see AnnotationIntrospectorTestAsserts */ public class AnnotationIntrospectorTest { private final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider()); private final PropertyNamingStrategy propertyNamingStrategy = jsonbContext.getConfigProperties().getPropertyNamingStrategy(); /** * class under test. */ private AnnotationIntrospector instrospector = new AnnotationIntrospector(jsonbContext); @Test public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructor() { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy); assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator); } @Test public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethod() { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy); assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator); } @Test public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodIgnoringConstructorPorperties() { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.class, propertyNamingStrategy); assertParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.example(), creator); } @Test public void testJsonbAnnotatedProtectedConstructorLeadsToAnException() { assertThrows(JsonbException.class, () -> { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.example(), creator); }); } // TODO Under discussion: https://github.com/eclipse-ee4j/yasson/issues/326 @Disabled @Test public void testNoArgConstructorShouldBePreferredOverUnusableJsonbAnnotatedProtectedConstructor() { JsonbCreator creator = instrospector.getCreator(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy); assertParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.example(), creator); } @Test public void testMoreThanOneAnnotatedCreatorMethodShouldLeadToAnException() { assertThrows(JsonbException.class, () -> instrospector.getCreator(ObjectWithTwoJsonbCreatorAnnotatedSpots.class, propertyNamingStrategy), () -> "More than one @" + JsonbCreator.class.getSimpleName()); } @Test public void testCreatorShouldBeNullOnMissingConstructorAnnotation() { assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestAsserts.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ProvidesParameterRepresentation; import org.eclipse.yasson.internal.model.CreatorModel; import org.eclipse.yasson.internal.model.JsonbCreator; import java.lang.reflect.Type; import java.util.Map; import java.util.Map.Entry; @Disabled class AnnotationIntrospectorTestAsserts { /** * Creates a new Instance of the Object using the given {@link JsonbCreator} and compares the result with the given * expected Object. The comparison is done by comparing both {@link ProvidesParameterRepresentation#asParameters()}. * * @param expected {@link ProvidesParameterRepresentation} * @param creator {@link JsonbCreator} */ public static void assertCreatedInstanceContainsAllParameters(T expected, JsonbCreator creator) { assertNotNull(creator, JsonbCreator.class.getSimpleName() + " expected"); @SuppressWarnings("unchecked") T created = (T) creator.call(expected.asParameters(), expected.getClass()); assertArrayEquals(expected.asParameters(), created.asParameters()); } /** * Compares the expected parameters with the meta-model contained in the {@link JsonbCreator}. * * @param expectedParameters {@link Map} with {@link String}-Key and {@link Type}-Value * @param creator CreatorModel */ public static void assertParameters(Map expectedParameters, JsonbCreator creator) { assertNotNull(creator, JsonbCreator.class.getSimpleName() + " expected"); for (Entry parameter : expectedParameters.entrySet()) { CreatorModel creatorModel = creator.findByName(parameter.getKey()); assertEquals(parameter.getKey(), creatorModel.getName()); assertEquals(parameter.getValue(), creatorModel.getType()); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestFixtures.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; import java.beans.ConstructorProperties; import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @Disabled class AnnotationIntrospectorTestFixtures { public static interface ProvidesParameterRepresentation { Object[] asParameters(); } private static final Map twoParameters(String name1, Type type1, String name2, Type type2) { Map parameters = new HashMap<>(); parameters.put(name1, type1); parameters.put(name2, type2); return parameters; } public static Constructor[] constructorsOf(Class clazz) { return AccessController.doPrivileged((PrivilegedAction[]>) clazz::getDeclaredConstructors); } public static class ObjectWithoutAnnotatedConstructor implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithoutAnnotatedConstructor("a string", Long.MAX_VALUE); } public ObjectWithoutAnnotatedConstructor(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithNotAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithJsonbCreatorAnnotatedConstructor implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithJsonbCreatorAnnotatedConstructor("a string", Long.MAX_VALUE); } @JsonbCreator public ObjectWithJsonbCreatorAnnotatedConstructor( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithJsonbCreatorAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithJsonbCreatorAnnotatedProtectedConstructor implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithJsonbCreatorAnnotatedProtectedConstructor("a string", Long.MAX_VALUE); } @JsonbCreator protected ObjectWithJsonbCreatorAnnotatedProtectedConstructor( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithJsonbCreatorAnnotatedProtectedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor implements ProvidesParameterRepresentation { private String string; private long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor("a string", Long.MAX_VALUE); } public ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor() { super(); } @JsonbCreator protected ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } public String getString() { return string; } public void setString(String string) { this.string = string; } public long getPrimitive() { return primitive; } public void setPrimitive(long primitive) { this.primitive = primitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithJsonbCreatorAnnotatedFactoryMethod implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithJsonbCreatorAnnotatedFactoryMethod("text", Long.MIN_VALUE); } @JsonbCreator public static final ObjectWithJsonbCreatorAnnotatedFactoryMethod create( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { return new ObjectWithJsonbCreatorAnnotatedFactoryMethod(aString, aPrimitive); } private ObjectWithJsonbCreatorAnnotatedFactoryMethod(String string, long primitiv) { this.string = string; this.primitive = primitiv; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithJsonbCreatorAnnotatedFactoryMethod [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithTwoJsonbCreatorAnnotatedSpots implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithJsonbCreatorAnnotatedConstructor("", Long.valueOf(0)); } @JsonbCreator public static final ObjectWithTwoJsonbCreatorAnnotatedSpots create( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { return new ObjectWithTwoJsonbCreatorAnnotatedSpots(aString, aPrimitive); } @JsonbCreator public ObjectWithTwoJsonbCreatorAnnotatedSpots( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithTwoJsonbCreatorAnnotatedSpots [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithConstructorPropertiesAnnotation implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithConstructorPropertiesAnnotation(" ", Long.valueOf(-12)); } @ConstructorProperties({ "string", "primitive" }) public ObjectWithConstructorPropertiesAnnotation(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithConstructorPropertiesAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithTwoConstructorPropertiesAnnotation implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithTwoConstructorPropertiesAnnotation(" ", Long.valueOf(-12)); } @ConstructorProperties({ "string" }) public ObjectWithTwoConstructorPropertiesAnnotation(String aString) { this(aString, 0L); } @ConstructorProperties({ "string", "primitive" }) public ObjectWithTwoConstructorPropertiesAnnotation(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithTwoConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation("", Long.valueOf(0)); } @JsonbCreator public static final ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation create( // @JsonbProperty("string") String aString, // @JsonbProperty("primitive") long aPrimitive) { return new ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation(aString, aPrimitive); } @ConstructorProperties({ "string", "primitive" }) public ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithPublicNoArgAndAnnotatedPrivateConstructor implements ProvidesParameterRepresentation { private String string; private Long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithPublicNoArgAndAnnotatedPrivateConstructor(" ", Long.valueOf(-12)); } public ObjectWithPublicNoArgAndAnnotatedPrivateConstructor() { super(); } @ConstructorProperties({ "string", "primitive" }) private ObjectWithPublicNoArgAndAnnotatedPrivateConstructor(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } public Long getPrimitive() { return primitive; } public void setPrimitive(Long primitive) { this.primitive = primitive; } public String getString() { return string; } public void setString(String string) { this.string = string; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithPublicNoArgAndAnnotatedPrivateConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor implements ProvidesParameterRepresentation { private String string; private Long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(" ", Long.valueOf(-12)); } public static final ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor create(String aString, long aPrimitive) { return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(aString, aPrimitive); } public ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor() { super(); } @ConstructorProperties({ "string", "primitive" }) ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } public Long getPrimitive() { return primitive; } public void setPrimitive(Long primitive) { this.primitive = primitive; } public String getString() { return string; } public void setString(String string) { this.string = string; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithPublicNoArgAndAnnotatedProtectedConstructor implements ProvidesParameterRepresentation { private String string; private Long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(" ", Long.valueOf(-12)); } public static final ObjectWithPublicNoArgAndAnnotatedProtectedConstructor create(String aString, long aPrimitive) { return new ObjectWithPublicNoArgAndAnnotatedProtectedConstructor(aString, aPrimitive); } @ConstructorProperties({ "string", "primitive" }) protected ObjectWithPublicNoArgAndAnnotatedProtectedConstructor(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } public ObjectWithPublicNoArgAndAnnotatedProtectedConstructor() { super(); } public Long getPrimitive() { return primitive; } public void setPrimitive(Long primitive) { this.primitive = primitive; } public String getString() { return string; } public void setString(String string) { this.string = string; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]"; } } public static class ObjectWithMissingConstructorAnnotation implements ProvidesParameterRepresentation { private final String string; private final long primitive; public static final Map parameters() { return twoParameters("string", String.class, "primitive", long.class); } public static final ProvidesParameterRepresentation example() { return new ObjectWithMissingConstructorAnnotation("a string", Long.MAX_VALUE); } public ObjectWithMissingConstructorAnnotation(String aString, long aPrimitive) { this.string = aString; this.primitive = aPrimitive; } @Override public Object[] asParameters() { return new Object[] { string, primitive }; } @Override public String toString() { return "ObjectWithMissingConstructorAnnotation [string=" + string + ", primitive=" + primitive + "]"; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorWithoutOptionalModulesTest.java ================================================ /* * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.spi.JsonProvider; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedFactoryMethod; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor; import org.eclipse.yasson.internal.model.JsonbCreator; import org.junit.jupiter.api.Test; /** * Tests the {@link AnnotationIntrospector} with missing optional module "java.deskop",
* that contains the ConstructorProperties-Annotation. *

* Requires --limit-modules java.base,java.logging,java.sql (to exclude java.desktop) to work.
* See pom.xml surefire plugin configuration. * * @see AnnotationIntrospectorTestFixtures */ public class AnnotationIntrospectorWithoutOptionalModulesTest { /** * class under test. */ private static final AnnotationIntrospector instrospector = new AnnotationIntrospector(new JsonbContext(new JsonbConfig(), JsonProvider.provider())); private final PropertyNamingStrategy propertyNamingStrategy = propertyName -> propertyName; @Test public void testNoConstructorPropertiesAnnotationWithoutOptionalModules() { String className = "java.beans.ConstructorProperties"; try { Class.forName(className); fail("Class [" + className + "] should not be available for this test."); } catch (ClassNotFoundException e) { // OK, as expected } } @Test public void testCreatorShouldBeNullOnMissingConstructorAnnotation() { assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy)); } @Test public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructorWithoutOptionalModules() { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy); assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator); } @Test public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodWithoutOptionalModules() { JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy); assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/ClassParserTest.java ================================================ /* * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.eclipse.yasson.internal.model.customization.ClassCustomization; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.defaultmapping.modifiers.model.FieldModifiersClass; import org.eclipse.yasson.defaultmapping.modifiers.model.MethodModifiersClass; import org.eclipse.yasson.internal.model.ClassModel; import org.eclipse.yasson.internal.model.JsonbAnnotatedElement; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; /** * Test for ClassParser component. * * @author Roman Grigoriadi */ public class ClassParserTest { private static final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider()); private static final ClassParser classParser = new ClassParser(jsonbContext); private static final AnnotationIntrospector introspector = new AnnotationIntrospector(jsonbContext); @Test public void testDefaultMappingFieldModifiers() { final JsonbAnnotatedElement> clsElement = introspector.collectAnnotations(FieldModifiersClass.class); ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement, ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null); classParser.parseProperties(model, clsElement); assertTrue(model.getPropertyModel("finalString").isReadable()); assertFalse(model.getPropertyModel("finalString").isWritable()); assertFalse(model.getPropertyModel("staticString").isReadable()); assertFalse(model.getPropertyModel("staticString").isWritable()); assertFalse(model.getPropertyModel("transientString").isReadable()); assertFalse(model.getPropertyModel("transientString").isWritable()); } @Test public void testDefaultMappingMethodModifiers() { final JsonbAnnotatedElement> clsElement = introspector.collectAnnotations(MethodModifiersClass.class); ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement, ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null); classParser.parseProperties(model, clsElement); assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isReadable()); assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isWritable()); assertTrue(model.getPropertyModel("publicFieldWithoutMethods").isReadable()); assertTrue(model.getPropertyModel("publicFieldWithoutMethods").isWritable()); assertTrue(model.getPropertyModel("getterWithoutFieldValue").isReadable()); assertTrue(model.getPropertyModel("getterWithoutFieldValue").isWritable()); MethodModifiersClass object = new MethodModifiersClass(); final AtomicReference accepted = new AtomicReference<>(); Consumer withoutFieldConsumer = accepted::set; object.setSetterWithoutFieldConsumer(withoutFieldConsumer); model.getPropertyModel("getterWithoutFieldValue").setValue(object, "ACCEPTED_VALUE"); assertEquals("ACCEPTED_VALUE", accepted.get()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/CollectionsWithJavaBaseTypesTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyVisibilityStrategy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.time.LocalDate; import java.util.*; /** * Contains java.base types inside collections. * Tests that yasson won't try to parse this types into ClassModel. */ public class CollectionsWithJavaBaseTypesTest { @Test public void testSimple() { JavaBasePropertiesInContainer properties = new JavaBasePropertiesInContainer(); properties.setNames(Arrays.asList("First", "second", null)); for(int i = 0; i < 3; i++) { List integerList = new ArrayList<>(); integerList.add(0); integerList.add(1); integerList.add(null); properties.setListOfListsOfIntegers(new ArrayList<>()); properties.getListOfListsOfIntegers().add(integerList); } properties.setOptionalInts(new ArrayList<>()); properties.getOptionalInts().add(Optional.of(Integer.MAX_VALUE)); properties.getOptionalInts().add(Optional.empty()); properties.setDates(new ArrayList<>()); properties.getDates().add(LocalDate.of(2020, 1, 1)); properties.getDates().add(null); Integer[][] innerArrayInts = new Integer[1][2]; innerArrayInts[0][0] = 1; innerArrayInts[0][1] = null; properties.setInnerArrayInts(innerArrayInts); //Making all fields accessible. When used on JPMS will fail if java.base classes are parsed into class model. Jsonb jsonb = new JsonBindingBuilder().withConfig(new JsonbConfig().withNullValues(Boolean.TRUE) .withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { return true; } @Override public boolean isVisible(Method method) { return true; } })).build(); String expected = "{\"dates\":[\"2020-01-01\",null],\"innerArrayInts\":[[1,null]],\"listOfListsOfIntegers\":[[0,1,null]],\"names\":[\"First\",\"second\",null],\"optionalInts\":[2147483647,null]}"; assertEquals(expected, jsonb.toJson(properties)); JavaBasePropertiesInContainer result = jsonb.fromJson(expected, JavaBasePropertiesInContainer.class); assertEquals(properties.getNames(), result.getNames()); assertEquals(properties.getOptionalInts(), result.getOptionalInts()); assertEquals(properties.getListOfListsOfIntegers(), result.getListOfListsOfIntegers()); assertArrayEquals(properties.getInnerArrayInts(), result.getInnerArrayInts()); } public static final class JavaBasePropertiesInContainer { private List names; private List> optionalInts; private List dates; public Integer[][] getInnerArrayInts() { return innerArrayInts; } public void setInnerArrayInts(Integer[][] innerArrayInts) { this.innerArrayInts = innerArrayInts; } private Integer[][] innerArrayInts; public List getDates() { return dates; } public void setDates(List dates) { this.dates = dates; } public List> getOptionalInts() { return optionalInts; } public void setOptionalInts(List> optionalInts) { this.optionalInts = optionalInts; } List> listOfListsOfIntegers; public List> getListOfListsOfIntegers() { return listOfListsOfIntegers; } public void setListOfListsOfIntegers(List> listOfListsOfIntegers) { this.listOfListsOfIntegers = listOfListsOfIntegers; } public List getNames() { return names; } public void setNames(List names) { this.names = names; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospectorTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters; import static org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.constructorsOf; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithConstructorPropertiesAnnotation; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedPrivateConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedProtectedConstructor; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithTwoConstructorPropertiesAnnotation; import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor; import org.eclipse.yasson.internal.model.JsonbCreator; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; public class ConstructorPropertiesAnnotationIntrospectorTest { private final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider()); private final AnnotationFinder constructorPropertiesFinder = AnnotationFinder.findConstructorProperties(); /** * class under test. */ private ConstructorPropertiesAnnotationIntrospector instrospector = new ConstructorPropertiesAnnotationIntrospector(jsonbContext, constructorPropertiesFinder); @Test public void testObjectShouldBeCreateableFromConstructorPropertiesAnnotatedConstructor() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithConstructorPropertiesAnnotation.class)); assertParameters(ObjectWithConstructorPropertiesAnnotation.parameters(), creator); assertCreatedInstanceContainsAllParameters(ObjectWithConstructorPropertiesAnnotation.example(), creator); } @Test public void testShouldAlsoWorkWithStaticFactoryMethodAndPredefinedAnnotationFinder() { instrospector = ConstructorPropertiesAnnotationIntrospector.forContext(jsonbContext); JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithConstructorPropertiesAnnotation.class)); assertNotNull(creator); } @Test public void testNullShouldBeReturnedWhenThereIsNoCreatorAnnotation() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithoutAnnotatedConstructor.class)); assertNull(creator); } @Test public void testNullShouldBeReturnedWhenThereIsNoConstructorPropertiesAnnotation() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithJsonbCreatorAnnotatedConstructor.class)); assertNull(creator); } @Test public void testNullShouldBeReturnedWhenThereAreMoreThanOneConstructorPropertiesAnnotation() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithTwoConstructorPropertiesAnnotation.class)); assertNull(creator); } @Test public void testAnnotatedInaccessiblePrivateConstructorShouldBeIgnored() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedPrivateConstructor.class)); assertNull(creator); } @Test public void testAnnotatedInaccessiblePackageProtectedConstructorShouldBeIgnored() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor.class)); assertNull(creator); } @Test public void testAnnotatedInaccessibleProtectedConstructorShouldBeIgnored() { JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedProtectedConstructor.class)); assertNull(creator); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java ================================================ /* * Copyright (c) 2019, 2020 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.lang.reflect.Field; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.internal.model.ClassModel; import org.junit.jupiter.api.Test; public class JsonBindingTest { public static class EagerParseClass { public String foo; } @Test public void testEagerInit() throws Exception { Jsonb jsonb = JsonbBuilder.create(new YassonConfig() .withEagerParsing(EagerParseClass.class)); assertNotNull(getClassModel(jsonb, EagerParseClass.class)); EagerParseClass obj = new EagerParseClass(); obj.foo = "foo"; assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj)); } @Test public void testNoEagerInit() throws Exception { Jsonb jsonb = JsonbBuilder.create(); assertNull(getClassModel(jsonb, EagerParseClass.class)); EagerParseClass obj = new EagerParseClass(); obj.foo = "foo"; assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj)); assertNotNull(getClassModel(jsonb, EagerParseClass.class)); } private ClassModel getClassModel(Jsonb jsonb, Class clazz) throws Exception { // Do some hacks to ensure that the class had a ClassModel registered JsonBinding yasson = (JsonBinding) jsonb; Field jsonbContext = yasson.getClass().getDeclaredField("jsonbContext"); jsonbContext.setAccessible(true); JsonbContext ctx = (JsonbContext) jsonbContext.get(yasson); return ctx.getMappingContext().getClassModel(clazz); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/ReflectionUtilsTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * @author Roman Grigoriadi */ public class ReflectionUtilsTest { public static class Types { public List resolvedParameterizedField; public List> resolvedNestedParameterizedField; public String resolvedStr; public List unresolvedParameterizedField; public List> unresolvedNestedParameterizedField; public T unresolvedField; public List unresolvedWildcardField; } @Test public void testIsTypeResolved() { Types types = new Types<>(); assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedParameterizedField"))); assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedNestedParameterizedField"))); assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedStr"))); assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedParameterizedField"))); assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedNestedParameterizedField"))); assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedField"))); assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedWildcardField"))); } private static Type getFieldType(String fieldName) { try { Field field = Types.class.getField(fieldName); return field.getGenericType(); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/AdaptedPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.json.bind.annotation.JsonbTypeAdapter; /** * @author Roman Grigoriadi */ public class AdaptedPojo { @JsonbTypeAdapter(CdiDependentAdapter.class) public String adaptedValue1 = "1111"; @JsonbTypeAdapter(CdiDependentAdapter.class) public String adaptedValue2 = "1001"; @JsonbTypeAdapter(NonCdiAdapter.class) public String adaptedValue3 = "1010"; } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/CalledMethods.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import java.util.HashMap; import java.util.Map; /** * Mockito like method call counter for CDI beans. * * @author Roman Grigoriadi */ @ApplicationScoped public class CalledMethods { /** * Maps method name to called count. */ private Map results = new HashMap<>(); public void registerCall(@Observes MethodCalledEvent methodCalledEvent) { results.compute(methodCalledEvent.getMethodName(), (s, c) -> c == null ? 1 : c + 1); } public Map getResults() { return results; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/CdiDependentAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.json.bind.adapter.JsonbAdapter; /** * Test scope components for testing CDI injection and cleanup. * * @author Roman Grigoriadi */ @ApplicationScoped public class CdiDependentAdapter implements JsonbAdapter { @Inject private CdiTestService cdiTestService; @Inject @Hello2 private IHelloService helloService; @Override public Integer adaptToJson(String obj) throws Exception { cdiTestService.runService(); helloService.sayHello(); return Integer.valueOf(obj); } @Override public String adaptFromJson(Integer obj) throws Exception { return String.valueOf(obj); } public CdiTestService getCdiTestService() { return cdiTestService; } public IHelloService getHelloService() { return helloService; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.CDI; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyVisibilityStrategy; import javax.naming.InitialContext; import javax.naming.NamingException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; /** * @author Roman Grigoriadi */ public class CdiInjectionTest { @Test public void testInjectionAndCleanup() throws Exception { WeldManager weldManager = new WeldManager(); weldManager.startWeld(CalledMethods.class, CdiTestService.class, HelloService1.class, HelloService2.class); Jsonb jsonb = JsonbBuilder.create(); final String result = jsonb.toJson(new AdaptedPojo()); jsonb.close(); assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result); //HelloService1 is @ApplicationScoped CalledMethods calledMethods = getCalledMethods(); Map results = calledMethods.getResults(); assertTrue(results.containsKey(HelloService1.class.getName() + ".sayHello")); assertFalse(results.containsKey(HelloService1.class.getName() + ".preDestroy")); //HelloService2 is @Dependent assertTrue(results.containsKey(HelloService2.class.getName() + ".sayHello")); assertTrue(results.containsKey(HelloService2.class.getName() + ".preDestroy")); //CdiTestService is @ApplicationScoped assertTrue(results.containsKey(CdiTestService.class.getName() + ".runService")); assertFalse(results.containsKey(CdiTestService.class.getName() + ".preDestroy")); // getCalledMethods().printCalled(); weldManager.shutdownWeld(); assertTrue(results.containsKey(CdiTestService.class.getName() + ".preDestroy")); assertTrue(results.containsKey(HelloService1.class.getName() + ".preDestroy")); // getCalledMethods().printCalled(); } @Test public void testInJndiEnvironment() throws NamingException { InitialContext context = new InitialContext(); context.bind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME, new JndiBeanManager()); String result; try { Jsonb jsonb = JsonbBuilder.create(); result = jsonb.toJson(new AdaptedPojo()); } finally { context.unbind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME); } assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result); } @Test public void testNonCdiEnvironment() { JsonbConfig config = new JsonbConfig(); //allow only field with components that doesn't has cdi dependencies. config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { return "adaptedValue3".equals(field.getName()); } @Override public boolean isVisible(Method method) { return false; } }); Jsonb jsonb = JsonbBuilder.create(config); final String result = jsonb.toJson(new AdaptedPojo()); assertEquals("{\"adaptedValue3\":1010}", result); } private CalledMethods getCalledMethods() { final BeanManager beanManager = CDI.current().getBeanManager(); final Bean resolve = beanManager.resolve(beanManager.getBeans(CalledMethods.class)); return (CalledMethods) beanManager.getReference(resolve, CalledMethods.class, beanManager.createCreationalContext(resolve)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/CdiTestService.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; /** * CDI test service * @author Roman Grigoriadi */ @ApplicationScoped public class CdiTestService { @Inject private Event calledEvent; @Inject @Hello1 private IHelloService helloService; public CdiTestService() { } public void runService() { helloService.sayHello(); calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".runService")); } @PreDestroy public void preDestroy() { calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy")); } public IHelloService getHelloService() { return helloService; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/Hello1.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.inject.Default; import jakarta.inject.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Roman Grigoriadi */ @Default @Qualifier @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Hello1 { } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/Hello2.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.inject.Default; import jakarta.inject.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Roman Grigoriadi */ @Default @Qualifier @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Hello2 { } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/HelloService1.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; /** * @author Roman Grigoriadi */ @Hello1 @ApplicationScoped public class HelloService1 implements IHelloService { @Inject private Event calledEvent; @Override public void sayHello() { calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".sayHello")); } @PreDestroy public void preDestroy() { calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy")); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/HelloService2.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; /** * Dependant scoped bean, should be destroyed with its wrapper bean. * * @author Roman Grigoriadi */ @Hello2 @Dependent public class HelloService2 implements IHelloService { @Inject private Event calledEvent; @Override public void sayHello() { calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".sayHello")); } @PreDestroy public void preDestroy() { calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy")); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/IHelloService.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; /** * @author Roman Grigoriadi */ public interface IHelloService { void sayHello(); } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/JndiBeanManager.java ================================================ /* * Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.el.ELResolver; import jakarta.el.ExpressionFactory; import jakarta.enterprise.context.spi.Context; import jakarta.enterprise.context.spi.Contextual; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.AnnotatedField; import jakarta.enterprise.inject.spi.AnnotatedMember; import jakarta.enterprise.inject.spi.AnnotatedMethod; import jakarta.enterprise.inject.spi.AnnotatedParameter; import jakarta.enterprise.inject.spi.AnnotatedType; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanAttributes; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.Decorator; import jakarta.enterprise.inject.spi.Extension; import jakarta.enterprise.inject.spi.InjectionPoint; import jakarta.enterprise.inject.spi.InjectionTarget; import jakarta.enterprise.inject.spi.InjectionTargetFactory; import jakarta.enterprise.inject.spi.InterceptionFactory; import jakarta.enterprise.inject.spi.InterceptionType; import jakarta.enterprise.inject.spi.Interceptor; import jakarta.enterprise.inject.spi.ObserverMethod; import jakarta.enterprise.inject.spi.ProducerFactory; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.Set; public class JndiBeanManager implements BeanManager { @Override public Object getReference(Bean bean, Type beanType, CreationalContext ctx) { throw new UnsupportedOperationException("Not implemented"); } @Override public Object getInjectableReference(InjectionPoint ij, CreationalContext ctx) { throw new UnsupportedOperationException("Not implemented"); } @Override public CreationalContext createCreationalContext(Contextual contextual) { return null; } @Override public Set> getBeans(Type beanType, Annotation... qualifiers) { throw new UnsupportedOperationException("Not implemented"); } @Override public Set> getBeans(String name) { throw new UnsupportedOperationException("Not implemented"); } @Override public Bean getPassivationCapableBean(String id) { throw new UnsupportedOperationException("Not implemented"); } @Override public Bean resolve(Set> beans) { throw new UnsupportedOperationException("Not implemented"); } @Override public void validate(InjectionPoint injectionPoint) { } @Override public Set> resolveObserverMethods(T event, Annotation... qualifiers) { throw new UnsupportedOperationException("Not implemented"); } @Override public List> resolveDecorators(Set types, Annotation... qualifiers) { throw new UnsupportedOperationException("Not implemented"); } @Override public List> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean isScope(Class annotationType) { return false; } @Override public boolean isNormalScope(Class annotationType) { return false; } @Override public boolean isPassivatingScope(Class annotationType) { return false; } @Override public boolean isQualifier(Class annotationType) { return false; } @Override public boolean isInterceptorBinding(Class annotationType) { return false; } @Override public boolean isStereotype(Class annotationType) { return false; } @Override public Set getInterceptorBindingDefinition(Class bindingType) { throw new UnsupportedOperationException("Not implemented"); } @Override public Set getStereotypeDefinition(Class stereotype) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2) { return false; } @Override public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2) { return false; } @Override public int getQualifierHashCode(Annotation qualifier) { return 0; } @Override public int getInterceptorBindingHashCode(Annotation interceptorBinding) { return 0; } @Override public Context getContext(Class scopeType) { throw new UnsupportedOperationException("Not implemented"); } @Override public Collection getContexts(Class aClass) { throw new UnsupportedOperationException("Not implemented"); } @Override public ELResolver getELResolver() { throw new UnsupportedOperationException("Not implemented"); } @Override public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory) { throw new UnsupportedOperationException("Not implemented"); } @Override public AnnotatedType createAnnotatedType(Class type) { return null; } @Override public InjectionTargetFactory getInjectionTargetFactory(AnnotatedType annotatedType) { return new MockInjectionTargetFactory<>(); } @Override public ProducerFactory getProducerFactory(AnnotatedField field, Bean declaringBean) { throw new UnsupportedOperationException("Not implemented"); } @Override public ProducerFactory getProducerFactory(AnnotatedMethod method, Bean declaringBean) { throw new UnsupportedOperationException("Not implemented"); } @Override public BeanAttributes createBeanAttributes(AnnotatedType type) { throw new UnsupportedOperationException("Not implemented"); } @Override public BeanAttributes createBeanAttributes(AnnotatedMember type) { throw new UnsupportedOperationException("Not implemented"); } @Override public Bean createBean(BeanAttributes attributes, Class beanClass, InjectionTargetFactory injectionTargetFactory) { throw new UnsupportedOperationException("Not implemented"); } @Override public Bean createBean(BeanAttributes attributes, Class beanClass, ProducerFactory producerFactory) { throw new UnsupportedOperationException("Not implemented"); } @Override public InjectionPoint createInjectionPoint(AnnotatedField field) { throw new UnsupportedOperationException("Not implemented"); } @Override public InjectionPoint createInjectionPoint(AnnotatedParameter parameter) { throw new UnsupportedOperationException("Not implemented"); } @Override public T getExtension(Class extensionClass) { throw new UnsupportedOperationException("Not implemented"); } @Override public InterceptionFactory createInterceptionFactory(CreationalContext ctx, Class clazz) { throw new UnsupportedOperationException("Not implemented"); } @Override public Event getEvent() { throw new UnsupportedOperationException("Not implemented"); } @Override public Instance createInstance() { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean isMatchingBean(Set set, Set set1, Type type, Set set2) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean isMatchingEvent(Type type, Set set, Type type1, Set set1) { throw new UnsupportedOperationException("Not implemented"); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/MethodCalledEvent.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; /** * @author Roman Grigoriadi */ public class MethodCalledEvent { private final String methodName; public MethodCalledEvent(String methodName) { this.methodName = methodName; } public String getMethodName() { return methodName; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.InjectionPoint; import jakarta.enterprise.inject.spi.InjectionTarget; import jakarta.json.bind.adapter.JsonbAdapter; import java.util.Set; /** * For JNDI Bean Manager resolution testing purposes. */ public class MockInjectionTarget implements InjectionTarget { @Override public void inject(JsonbAdapter instance, CreationalContext ctx) { } @Override public void postConstruct(JsonbAdapter instance) { } @Override public void preDestroy(JsonbAdapter instance) { } @Override public JsonbAdapter produce(CreationalContext ctx) { return new NonCdiAdapter(); } @Override public void dispose(JsonbAdapter instance) { } @Override public Set getInjectionPoints() { return null; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java ================================================ /* * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.InjectionTarget; import jakarta.enterprise.inject.spi.InjectionTargetFactory; /** * TODO javadoc */ public class MockInjectionTargetFactory implements InjectionTargetFactory { @Override public InjectionTarget createInjectionTarget(Bean bean) { return (InjectionTarget) new MockInjectionTarget(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContext.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import javax.naming.*; import java.util.Hashtable; import java.util.concurrent.ConcurrentHashMap; /** * @author Roman Grigoriadi */ public class MockJndiContext implements Context { private static final ConcurrentHashMap objects = new ConcurrentHashMap<>(); @Override public Object lookup(Name name) throws NamingException { return null; } @Override public Object lookup(String name) throws NamingException { final Object o = objects.get(name); if (o == null) { throw new NamingException("No object found under: "+name); } return o; } @Override public void bind(Name name, Object obj) throws NamingException { } @Override public void bind(String name, Object obj) throws NamingException { objects.put(name, obj); } @Override public void rebind(Name name, Object obj) throws NamingException { } @Override public void rebind(String name, Object obj) throws NamingException { } @Override public void unbind(Name name) throws NamingException { } @Override public void unbind(String name) throws NamingException { objects.remove(name); } @Override public void rename(Name oldName, Name newName) throws NamingException { } @Override public void rename(String oldName, String newName) throws NamingException { } @Override public NamingEnumeration list(Name name) throws NamingException { return null; } @Override public NamingEnumeration list(String name) throws NamingException { return null; } @Override public NamingEnumeration listBindings(Name name) throws NamingException { return null; } @Override public NamingEnumeration listBindings(String name) throws NamingException { return null; } @Override public void destroySubcontext(Name name) throws NamingException { } @Override public void destroySubcontext(String name) throws NamingException { } @Override public Context createSubcontext(Name name) throws NamingException { return null; } @Override public Context createSubcontext(String name) throws NamingException { return null; } @Override public Object lookupLink(Name name) throws NamingException { return null; } @Override public Object lookupLink(String name) throws NamingException { return null; } @Override public NameParser getNameParser(Name name) throws NamingException { return null; } @Override public NameParser getNameParser(String name) throws NamingException { return null; } @Override public Name composeName(Name name, Name prefix) throws NamingException { return null; } @Override public String composeName(String name, String prefix) throws NamingException { return null; } @Override public Object addToEnvironment(String propName, Object propVal) throws NamingException { return null; } @Override public Object removeFromEnvironment(String propName) throws NamingException { return null; } @Override public Hashtable getEnvironment() throws NamingException { return null; } @Override public void close() throws NamingException { } @Override public String getNameInNamespace() throws NamingException { return null; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContextFactory.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import java.util.Hashtable; /** * @author Roman Grigoriadi */ public class MockJndiContextFactory implements InitialContextFactory { @Override public Context getInitialContext(Hashtable environment) throws NamingException { return new MockJndiContext(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/NonCdiAdapter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import jakarta.json.bind.adapter.JsonbAdapter; /** * @author Roman Grigoriadi */ public class NonCdiAdapter implements JsonbAdapter { @Override public Integer adaptToJson(String obj) throws Exception { return Integer.valueOf(obj); } @Override public String adaptFromJson(Integer obj) throws Exception { return String.valueOf(obj); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.cdi; import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory; import org.jboss.weld.environment.se.Weld; import org.jboss.weld.environment.se.WeldContainer; import javax.naming.InitialContext; import javax.naming.NamingException; /** * Starts and Shutdowns Weld container, for CDI testing purposes. * * @author Roman Grigoriadi */ public class WeldManager { private Weld weld; private InitialContext initialContext; public void startWeld(Class... scannedClasses) throws NamingException { weld = new Weld().beanClasses(scannedClasses).disableDiscovery(); WeldContainer container = weld.initialize(); initialContext = new InitialContext(); initialContext.bind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME, container.getBeanManager()); } public void shutdownWeld() throws NamingException { weld.shutdown(); initialContext.unbind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME); initialContext.close(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/JsonProcessingResult.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; /** * A result produced by marshalling or unmarshalling operation called on jsonb. * @param For marshalling its String, for unmarshalling Customer. */ class JsonProcessingResult { private final T result; private final String dispatcherThreadName; private final String jobId; public JsonProcessingResult(T result, String dispatcherThreadName, String jobId) { this.result = result; this.dispatcherThreadName = dispatcherThreadName; this.jobId = jobId; } public T getResult() { return result; } public String getDispatcherThreadName() { return dispatcherThreadName; } public String getJobId() { return jobId; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTask.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; import org.eclipse.yasson.defaultmapping.specific.model.Customer; import jakarta.json.bind.Jsonb; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; /** * Runs JSON marshalling, returns Json result. */ class MarshallerTask implements Callable> { private final Jsonb jsonb; private final String jobId; private final MultiTenancyTest.ConfigurationType configurationType; private final CountDownLatch latch; private final Customer customer; public MarshallerTask(CountDownLatch latch, MultiTenancyTest.ConfigurationType jsonbConfiguration, String jobId, Customer customer) { this.jsonb = jsonbConfiguration.getJsonbInstance(); this.jobId = jobId; this.configurationType = jsonbConfiguration; this.latch = latch; this.customer = customer; } @Override public JsonProcessingResult call() throws Exception { try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } //marshalling will produce wrong json result if JsonbConfig in ThreadLocal JsonbContext will not match expected result String json = jsonb.toJson(customer); return new JsonProcessingResult<>(new MarshallerTaskResult(json, configurationType), Thread.currentThread().getName(), jobId); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTaskResult.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; /** * Wraps a resulted JSON string with jsonb configuration type, to know against which json to check result. */ class MarshallerTaskResult { private final String producedJson; private final MultiTenancyTest.ConfigurationType configurationType; public MarshallerTaskResult(String producedJson, MultiTenancyTest.ConfigurationType configurationType) { this.producedJson = producedJson; this.configurationType = configurationType; } public String getProducedJson() { return producedJson; } public MultiTenancyTest.ConfigurationType getConfigurationType() { return configurationType; } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/MultiTenancyTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import org.eclipse.yasson.defaultmapping.specific.CustomerTest; import org.eclipse.yasson.defaultmapping.specific.model.Customer; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyNamingStrategy; import jakarta.json.bind.config.PropertyVisibilityStrategy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Logger; /** * Tests consistency along sharing instances of Jsonb with different JsonbConfig between threads. * Tests simulates web server like environment, where threads dispatching http requests are pooled and reused. * * Each of jsonb instances has different JsonbConfig, forcing to produce / parse * different JSON string during marshalling to / unmarshalling from json. * * If ThreadLocal JsonbContext will be inconsistently shared between pooled threads * this test should fail while testing results of calls to shared jsonb instances marshalling / unmarshalling methods. * * @author Roman Grigoriadi */ public class MultiTenancyTest extends CustomerTest { private static Logger logger = Logger.getLogger(MultiTenancyTest.class.getName()); /** * Expected JSON for defaultJsonBinding instance, default key names. */ private static final String DEFAULT_CONFIG_JSON_STRING = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"MULTI_TENANCY_TEST\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}"; /** * Expected JSON for customizedJsonBinding instance, configured with LOWER_CASE_WITH_UNDERSCORES property naming strategy key names and also skips some properties according to custom PropertyVisibilityStrategy. */ private static final String CUSTOMIZED_CONFIG_JSON_STRING = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"list_of_lists_of_integers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"MULTI_TENANCY_TEST\",\"string_integer_map\":{\"first\":1,\"second\":2}}"; /** * A size of thread pool. Should be lesser than TOTAL_JOB_COUNT, so that threads are reused for different tasks, * causing more likeness to fail ThreadLocal state managing. */ private static final int THREAD_COUNT = 35; /** * A total count of marshalling / unmarshalling operations. */ private static final int TOTAL_JOB_COUNT = 2000; /** * After results of marshalling / unmarshalling are checked this count is incremented. * When test finishes, this count is required to be equal to TOTAL_JOB_COUNT. */ private final AtomicLong resultsCheckedCount = new AtomicLong(); /** * Latch firing first THREAD_COUNT tasks instantly. */ private final static CountDownLatch fireJsonbProcessing = new CountDownLatch(1); private static final Jsonb defaultJsonBinding; private static final Jsonb customizedJsonBinding; public enum ConfigurationType { DEFAULT(defaultJsonBinding, DEFAULT_CONFIG_JSON_STRING), CUSTOMIZED(customizedJsonBinding, CUSTOMIZED_CONFIG_JSON_STRING); ConfigurationType(Jsonb jsonbInstance, String expectedJson) { this.jsonbInstance = jsonbInstance; this.expectedJson = expectedJson; } private Jsonb jsonbInstance; private String expectedJson; public Jsonb getJsonbInstance() { return jsonbInstance; } public String getExpectedJson() { return expectedJson; } } /** * Thread pool for JSONB processing. */ private static ExecutorService jsonbProcessingThreadPool; private static CompletionService> marshallingCompletion; private static CompletionService> unmarshallingCompletion; /** * Executor for checking results. */ private static ExecutorService resultCheckService; /** * Jsonb instances configuration initialisation. */ static { JsonbConfig defaultConfig = new JsonbConfig(); JsonbConfig customizedConfig = new JsonbConfig(); customizedConfig.setProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY, PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES); customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() { @Override public boolean isVisible(Field field) { final String name = field.getName(); return !(name.equals("strings") || name.equals("integers")); } @Override public boolean isVisible(Method method) { final String name = method.getName(); return !(name.equals("getStrings") || name.equals("getIntegers")); } }); defaultJsonBinding = JsonbBuilder.create(defaultConfig); customizedJsonBinding = JsonbBuilder.create(customizedConfig); } @BeforeAll public static void setUp() throws Exception { jsonbProcessingThreadPool = Executors.newFixedThreadPool(THREAD_COUNT); marshallingCompletion = new ExecutorCompletionService<>(jsonbProcessingThreadPool); unmarshallingCompletion = new ExecutorCompletionService<>(jsonbProcessingThreadPool); resultCheckService = Executors.newFixedThreadPool(2); } @Test public void testDataConsistency() throws Exception { submitJsonbProcessingTasks(); fireJsonbProcessing.countDown(); submitResultCheckingTasks(); jsonbProcessingThreadPool.shutdown(); jsonbProcessingThreadPool.awaitTermination(1000, TimeUnit.MILLISECONDS); resultCheckService.shutdown(); resultCheckService.awaitTermination(5000, TimeUnit.MILLISECONDS); //Final check, that successful task result check count matches count of tasks that were submitted. assertEquals(TOTAL_JOB_COUNT, resultsCheckedCount.get()); } /** * Picks marshalling / unmarshalling results from completion service queue and check if they * are equal their expected prototypes. * * If ThreadLocal JsonbContext is incorrectly shared between pooled threads, configuration of jsonb instances * would be stale and results will not match. */ private void submitResultCheckingTasks() { resultCheckService.execute(new ResultChecker<>(marshallingCompletion) { @Override protected void checkResult(JsonProcessingResult result) { MarshallerTaskResult marshallerResult = result.getResult(); //actual check, produced json by marshaller is equal to expected by configuration, which triggered marshalling task. assertEquals(marshallerResult.getConfigurationType().getExpectedJson(), marshallerResult.getProducedJson()); resultsCheckedCount.incrementAndGet(); logger.fine(String.format("Job %-32s dispatched by thread %-16s completed successfully.", result.getJobId(), result.getDispatcherThreadName())); } }); resultCheckService.execute(new ResultChecker<>(unmarshallingCompletion) { @Override protected void checkResult(JsonProcessingResult result) { //actual check, unmarshalled json result have all expected values. //if JsonbConfig is stale, key names will not match, and this assert will not pass. assertCustomerValues(result.getResult(), "MULTI_TENANCY_TEST"); resultsCheckedCount.incrementAndGet(); logger.fine(String.format("Job %-32s dispatched by thread %-16s completed successfully.", result.getJobId(), result.getDispatcherThreadName())); } }); } /** * Chooses shared jsonb instance, either with default or customized key names * and submits marshaller an unmarshaller task for it. */ private static void submitJsonbProcessingTasks() { for(int i = 0; i< TOTAL_JOB_COUNT; i+=2) { boolean even = (i % 4 == 0); ConfigurationType jsonbConfiguration = even ? ConfigurationType.DEFAULT : ConfigurationType.CUSTOMIZED; final String unmarshallerJobId = jsonbConfiguration.name() + "_Unmarshaller_ID_" + i; final UnmarshallerTask task = new UnmarshallerTask(fireJsonbProcessing, jsonbConfiguration.getExpectedJson(), jsonbConfiguration.getJsonbInstance(), unmarshallerJobId); unmarshallingCompletion.submit(task); final String marshallerJobId = jsonbConfiguration.name() + "_Marshaller_ID_" + i; final MarshallerTask marshallerTask = new MarshallerTask(fireJsonbProcessing, jsonbConfiguration, marshallerJobId, createCustomer("MULTI_TENANCY_TEST")); marshallingCompletion.submit(marshallerTask); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/ResultChecker.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Checks result of marshalling / unmarshalling operation. */ abstract class ResultChecker implements Runnable { private final CompletionService> completionService; public ResultChecker(CompletionService> completionService) { this.completionService = completionService; } /** * Polls Callable results from CompletionService result queue and checks validity. */ @Override public void run() { Future> resultFuture; try { while ((resultFuture = completionService.poll(500, TimeUnit.MILLISECONDS)) != null) { checkResult(resultFuture.get()); } } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } protected abstract void checkResult(JsonProcessingResult result); } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/concurrent/UnmarshallerTask.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.concurrent; import org.eclipse.yasson.defaultmapping.specific.model.Customer; import jakarta.json.bind.Jsonb; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; /** * Runs Jsonb unmarshaller, returns result. */ class UnmarshallerTask implements Callable> { private final String json; private final Jsonb jsonb; private final String jobId; private final CountDownLatch latch; public UnmarshallerTask(CountDownLatch latch, String json, Jsonb jsonb, String jobId) { this.json = json; this.jsonb = jsonb; this.jobId = jobId; this.latch = latch; } @Override public JsonProcessingResult call() throws Exception { latch.await(); //unmarshalling will fail if JsonbConfig in ThreadLocal JsonbContext will not match expected json Customer customer = jsonb.fromJson(json, Customer.class); return new JsonProcessingResult<>(customer, Thread.currentThread().getName(), jobId); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/model/ModulesUtil.java ================================================ /* * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model; import java.lang.invoke.MethodHandles; /** * Why is this class here?. * * This class is here to replace existing classes in test-runtime: * - src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java * - src/main/java9/org/eclipse/yasson/internal/model/ModulesUtil.java * * When tests are executed with maven-surefire-plugin the content of * 'classes' is in a different module-path than 'test-classes'. * * This causes the MethodHandles#publicLookup to fail. The reason is that * test classes to serialize/deserialize are coming from module 'test-classes' * and the module 'classes' has no access to it. The 'publicLookup' makes some * validations and because of this different modules, it fails. * * It should work if 'classes' and 'test-classes' are merged in one unique module. * */ class ModulesUtil { private ModulesUtil() { } static MethodHandles.Lookup lookup(){ return MethodHandles.lookup(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/model/customization/naming/NamingPojo.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization.naming; /** * @author Roman Grigoriadi */ public class NamingPojo { public NamingPojo() { } public NamingPojo(String upperCasedProperty, String _startingWithUnderscoreProperty, String CAPS_UNDERSCORE_PROPERTY) { this.upperCasedProperty = upperCasedProperty; this._startingWithUnderscoreProperty = _startingWithUnderscoreProperty; this.CAPS_UNDERSCORE_PROPERTY = CAPS_UNDERSCORE_PROPERTY; } public String upperCasedProperty; public String _startingWithUnderscoreProperty; public String CAPS_UNDERSCORE_PROPERTY; } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.model.customization.naming; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.config.PropertyNamingStrategy; import org.eclipse.yasson.internal.model.customization.StrategiesProvider; /** * Tests naming strategies. * * @author Roman Grigoriadi */ public class PropertyNamingStrategyTest { private final NamingPojo pojo = new NamingPojo("abc", "def", "ghi"); @Test public void testLowerCase() throws Exception { PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES); assertEquals("camel_case_property", strategy.translateName("camelCaseProperty")); assertEquals("camelcase_property", strategy.translateName("CamelcaseProperty")); assertEquals("camel_case_property", strategy.translateName("CamelCaseProperty")); assertEquals("_camel_case_property", strategy.translateName("_camelCaseProperty")); assertEquals("_camel_case_property", strategy.translateName("_CamelCaseProperty")); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES)); String lowercaseUnderscoresJson = "{\"_starting_with_underscore_property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper_cased_property\":\"abc\"}"; assertEquals(lowercaseUnderscoresJson, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson(lowercaseUnderscoresJson, NamingPojo.class); assertResult(result); } @Test public void testLowerDashes() throws Exception { PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES); assertEquals("camel-case-property", strategy.translateName("camelCaseProperty")); assertEquals("camelcase-property", strategy.translateName("CamelcaseProperty")); assertEquals("camel-case-property", strategy.translateName("CamelCaseProperty")); assertEquals("-camel-case-property", strategy.translateName("-camelCaseProperty")); assertEquals("-camel-case-property", strategy.translateName("-CamelCaseProperty")); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES)); String lowercaseDashesJson = "{\"_starting-with-underscore-property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper-cased-property\":\"abc\"}"; assertEquals(lowercaseDashesJson, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson(lowercaseDashesJson, NamingPojo.class); assertResult(result); } @Test public void testUpperCase() { PropertyNamingStrategy upperCaseStrategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE); assertEquals("UpperCamelCase", upperCaseStrategy.translateName("upperCamelCase")); assertEquals("UpperCamelCase", upperCaseStrategy.translateName("UpperCamelCase")); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)); String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"UpperCasedProperty\":\"abc\",\"_startingWithUnderscoreProperty\":\"def\"}"; assertEquals(upperCased, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); assertResult(result); } @Test public void testUpperCaseWithSpaces() { PropertyNamingStrategy upperCaseWithSpacesStrategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES); assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("upperCamelCase")); assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("UpperCamelCase")); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES)); String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"Upper Cased Property\":\"abc\",\"_starting With Underscore Property\":\"def\"}"; assertEquals(upperCased, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class); assertResult(result); } @Test public void testCaseInsensitive() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.CASE_INSENSITIVE)); String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"upperCasedProperty\":\"abc\"}"; assertEquals(upperCased, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson("{\"caPS_unDERscore_prOPERty\":\"ghi\",\"_startingwithUndERSCorePrOPERTy\":\"def\",\"upPERCASedProPerty\":\"abc\"}", NamingPojo.class); assertResult(result); } @Test public void testIdentityCaseSensitive() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY)); NamingPojo result = jsonb.fromJson("{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"UPPERCASEDPROPERTY\":\"abc\"}", NamingPojo.class); assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY); assertEquals("def", result._startingWithUnderscoreProperty); assertNull(result.upperCasedProperty); } @Test public void testCustom() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(propertyName -> propertyName + "_" + propertyName.toUpperCase())); String custom = "{\"CAPS_UNDERSCORE_PROPERTY_CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty__STARTINGWITHUNDERSCOREPROPERTY\":\"def\",\"upperCasedProperty_UPPERCASEDPROPERTY\":\"abc\"}"; assertEquals(custom, jsonb.toJson(pojo)); NamingPojo result = jsonb.fromJson(custom, NamingPojo.class); assertResult(result); } private static void assertResult(NamingPojo result) { assertEquals("abc", result.upperCasedProperty); assertEquals("def", result._startingWithUnderscoreProperty); assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY); } } ================================================ FILE: src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java ================================================ /* * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.internal.serializer; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import jakarta.json.bind.JsonbException; public class ObjectDeserializerTest { @Test public void testGetInstanceExceptionShouldContainClassNameOnMissingConstructor() { assertThrows(JsonbException.class, () -> defaultJsonb.fromJson("{\"key\":\"value\"}", DummyDeserializationClass.class), DummyDeserializationClass.class::getName); } public static class DummyDeserializationClass { private String key; public DummyDeserializationClass(String key) { this.key = key; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonpsubstitution/AdaptedJsonParser.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonpsubstitution; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; import jakarta.json.stream.JsonLocation; import jakarta.json.stream.JsonParser; import java.io.InputStream; import java.math.BigDecimal; import java.util.Map; import java.util.stream.Stream; public class AdaptedJsonParser implements JsonParser { /** * Adapts every parsed string value. */ public interface StringValueAdapter { String adaptStringValue(String value); } private final StringValueAdapter adapter; private final JsonParser jsonParser; public AdaptedJsonParser(StringValueAdapter adapter, InputStream inputStream) { this.adapter = adapter; this.jsonParser = Json.createParser(inputStream); } @Override public boolean hasNext() { return jsonParser.hasNext(); } @Override public Event next() { return jsonParser.next(); } @Override public String getString() { return adapter.adaptStringValue(jsonParser.getString()); } @Override public boolean isIntegralNumber() { return jsonParser.isIntegralNumber(); } @Override public int getInt() { return jsonParser.getInt(); } @Override public long getLong() { return jsonParser.getLong(); } @Override public BigDecimal getBigDecimal() { return jsonParser.getBigDecimal(); } @Override public JsonLocation getLocation() { return jsonParser.getLocation(); } @Override public JsonObject getObject() { return jsonParser.getObject(); } @Override public JsonValue getValue() { return jsonParser.getValue(); } @Override public JsonArray getArray() { return jsonParser.getArray(); } @Override public Stream getArrayStream() { return jsonParser.getArrayStream(); } @Override public Stream> getObjectStream() { return jsonParser.getObjectStream(); } @Override public Stream getValueStream() { return jsonParser.getValueStream(); } @Override public void skipArray() { jsonParser.skipArray(); } @Override public void skipObject() { jsonParser.skipObject(); } @Override public void close() { jsonParser.close(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonpsubstitution; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.Assertions; import org.eclipse.yasson.TestTypeToken; import jakarta.json.bind.JsonbException; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.List; public class PreinstantiatedJsonpTest { public static class Dog { public String name; public int age; public boolean goodDog = true; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Dog:\nname: " + name + "\nage: " + age + "\ngoodDog: " + goodDog; } } public static class Wrapper { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } private final String EXPECTED_JSON = "{\"age\":4,\"goodDog\":true,\"name\":\"Falco\",\"suffix\":\"Best dog ever!\"}"; private final String WRAPPED_JSON = "{\"instance\":" + EXPECTED_JSON + "}"; private Dog dog = new Dog("Falco", 4); @Test public void testPreinstantiatedJsonGeneratorAndParser() { ByteArrayOutputStream out = new ByteArrayOutputStream(); JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out); bindingYassonJsonb.toJson(dog, generator); generator.close(); assertEquals(EXPECTED_JSON, new String(out.toByteArray())); ByteArrayInputStream in = new ByteArrayInputStream(EXPECTED_JSON.getBytes()); JsonParser parser = new AdaptedJsonParser((value) -> { if ("Falco".equals(value)) { return value + ", a best dog ever!"; } return value; }, in); Dog result = bindingYassonJsonb.fromJson(parser, Dog.class); assertEquals("Falco, a best dog ever!", result.name); assertEquals(4, result.age); assertTrue(result.goodDog); } @Test public void testJsonParserAdvancedToCustomPosition() { ByteArrayInputStream in = new ByteArrayInputStream(WRAPPED_JSON.getBytes()); JsonParser parser = new AdaptedJsonParser((value) -> { if ("Falco".equals(value)) { return value + ", a best dog ever!"; } return value; }, in); parser.next(); //START_OBJECT parser.next(); //"instance" KEY Dog result = bindingYassonJsonb.fromJson(parser, Dog.class); parser.next(); //END_OJBECT assertEquals("Falco, a best dog ever!", result.name); assertEquals(4, result.age); assertTrue(result.goodDog); } @Test public void testGeneratorWrappedWithUserInteraction() { ByteArrayOutputStream out = new ByteArrayOutputStream(); JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out); generator.writeStartObject(); generator.writeKey("instance"); bindingYassonJsonb.toJson(dog, generator); generator.writeEnd(); generator.close(); assertEquals(WRAPPED_JSON, new String(out.toByteArray())); } @Test public void testInvalidJsonParserAdvancedToCustomPosition() { ByteArrayInputStream in = new ByteArrayInputStream(WRAPPED_JSON.getBytes()); JsonParser parser = new AdaptedJsonParser((value) -> { if ("Falco".equals(value)) { return value + ", a best dog ever!"; } return value; }, in); parser.next(); //START_OBJECT //should be advanced further try { bindingYassonJsonb.fromJson(parser, Dog.class); fail("JsonbException not thrown"); } catch (JsonbException e) { //OK, parser in inconsistent state } } @Test public void testInvalidGeneratorWrappedWithUserInteraction() { ByteArrayOutputStream out = new ByteArrayOutputStream(); JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out); generator.writeStartObject(); //key not written Assertions.shouldFail(() -> bindingYassonJsonb.toJson(dog, generator)); } @Test public void testRuntimeTypeParser() { Wrapper stringWrapper = new Wrapper<>(); stringWrapper.setValue("String value"); ByteArrayInputStream in = new ByteArrayInputStream("{\"value\":\"String value\"}".getBytes()); JsonParser parser = new AdaptedJsonParser((value) -> { if (value.equals("String value")) { return "Adapted string"; } return value; }, in); Wrapper result = bindingYassonJsonb.fromJson(parser, new TestTypeToken>() {}.getType()); assertEquals("Adapted string", result.getValue()); } /** * This test tests that provided generator is actually used. */ @Test public void testRuntimeTypeGenerator() { Wrapper stringWrapper = new Wrapper<>(); stringWrapper.setValue("String value"); ByteArrayOutputStream out = new ByteArrayOutputStream(); JsonGenerator generator = new SuffixJsonGenerator("Appended value.", out); bindingYassonJsonb.toJson(stringWrapper, new TestTypeToken>(){}.getType(), generator); generator.close(); assertEquals("{\"value\":\"String value\",\"suffix\":\"Appended value.\"}", out.toString()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonpsubstitution/SuffixJsonGenerator.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonpsubstitution; import jakarta.json.Json; import jakarta.json.JsonValue; import jakarta.json.stream.JsonGenerator; import java.io.OutputStream; import java.math.BigDecimal; import java.math.BigInteger; /** * Test purpose generator appending a suffix key-value to end of object. */ public class SuffixJsonGenerator implements JsonGenerator { private final JsonGenerator generator; /** * Create key-value pair for each object at its end. */ private final String objSuffix; private boolean written; public SuffixJsonGenerator(String objSuffix, OutputStream out) { this.objSuffix = objSuffix; this.generator = Json.createGenerator(out); } @Override public JsonGenerator writeStartObject() { return generator.writeStartObject(); } @Override public JsonGenerator writeStartObject(String name) { return generator.writeStartObject(name); } @Override public JsonGenerator writeKey(String name) { return generator.writeKey(name); } @Override public JsonGenerator writeStartArray() { return generator.writeStartArray(); } @Override public JsonGenerator writeStartArray(String name) { return generator.writeStartArray(name); } @Override public JsonGenerator write(String name, JsonValue value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, String value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, BigInteger value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, BigDecimal value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, int value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, long value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, double value) { return generator.write(name, value); } @Override public JsonGenerator write(String name, boolean value) { return generator.write(name, value); } @Override public JsonGenerator writeNull(String name) { return generator.writeNull(name); } @Override public JsonGenerator writeEnd() { if (!written) { generator.write("suffix", objSuffix); written = true; } return generator.writeEnd(); } @Override public JsonGenerator write(JsonValue value) { return generator.write(value); } @Override public JsonGenerator write(String value) { return generator.write(value); } @Override public JsonGenerator write(BigDecimal value) { return generator.write(value); } @Override public JsonGenerator write(BigInteger value) { return generator.write(value); } @Override public JsonGenerator write(int value) { return generator.write(value); } @Override public JsonGenerator write(long value) { return generator.write(value); } @Override public JsonGenerator write(double value) { return generator.write(value); } @Override public JsonGenerator write(boolean value) { return generator.write(value); } @Override public JsonGenerator writeNull() { return generator.writeNull(); } @Override public void close() { generator.close(); } @Override public void flush() { generator.flush(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; public final class InnerPojo { private String innerFirst; private String innerSecond; public String getInnerFirst() { return innerFirst; } public void setInnerFirst(String innerFirst) { this.innerFirst = innerFirst; } public String getInnerSecond() { return innerSecond; } public void setInnerSecond(String innerSecond) { this.innerSecond = innerSecond; } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; public class InnerPojoDeserializer implements JsonbDeserializer { @Override public InnerPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { InnerPojo innerPojo = new InnerPojo(); //KEY first parser.next(); //VALUE parser.next(); innerPojo.setInnerFirst(parser.getString()); //KEY second parser.next(); //VALUE parser.next(); innerPojo.setInnerSecond(parser.getString()); //END_OBJECT parser.next(); return innerPojo; } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class InnerPojoSerializer implements JsonbSerializer { @Override public void serialize(InnerPojo obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("first", obj.getInnerFirst()); generator.write("second", obj.getInnerSecond()); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/Issue673.java ================================================ /* * Copyright (c) 2026 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonString; import jakarta.json.JsonValue; import jakarta.json.bind.annotation.JsonbSubtype; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeInfo; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; import java.util.Collections; import java.util.stream.Collectors; /** * Test case for Issue #673: Custom deserializers with polymorphic types and JSON structure API. * *

This test validates the interaction between: *

    *
  • Custom {@link JsonbDeserializer} implementations
  • *
  • Polymorphic type handling via {@link JsonbTypeInfo} and {@link JsonbSubtype}
  • *
  • JSON-P structure API ({@link JsonArray}, {@link JsonObject}, {@link JsonValue})
  • *
* *

The test ensures that custom deserializers can properly access and process JSON structure * objects when deserializing complex types with polymorphic behavior. * * @see Issue #673 */ public class Issue673 { /** * Marker interface for objects that can be referenced. * Implemented by both {@link Reference} and {@link IRIReference}. */ public static interface Referenceable { } /** * A reference object with a description field. * Deserialized from JSON objects containing a "description" property. */ public static class Reference implements Referenceable { private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } /** * An IRI (Internationalized Resource Identifier) reference. * Deserialized from JSON string values representing URIs. */ public static class IRIReference implements Referenceable { private String value; public IRIReference() {} public IRIReference(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } /** * Interface for location types with polymorphic deserialization support. * Uses {@link JsonbTypeInfo} to determine concrete type based on "type" field in JSON. */ @JsonbTypeInfo(key = "type", value = { @JsonbSubtype(alias = Location.TYPE, type = Location.class) }) public static interface LocationInterface { } /** * Concrete location implementation with custom deserializers for complex fields. * *

Demonstrates: *

    *
  • Array-to-string conversion via {@link TagsDeserializer}
  • *
  • Polymorphic reference deserialization via {@link ReferenceableDeserializer}
  • *
*/ public static class Location implements LocationInterface { public final static String TYPE = "Location"; private String tags; private Referenceable referenceable; /** * Gets the tags as a comma-separated string. * Uses custom deserializer to convert JSON array to string. * * @return comma-separated tag string */ @JsonbTypeDeserializer(TagsDeserializer.class) public String getTags() { return tags; } public void setTags(String tags) { this.tags = tags; } /** * Gets the reference object. * Uses custom deserializer to handle polymorphic deserialization * from either string (IRI) or object (Reference) JSON values. * * @return the referenceable object */ @JsonbTypeDeserializer(ReferenceableDeserializer.class) public Referenceable getReference() { return referenceable; } public void setReference(Referenceable referenceable) { this.referenceable = referenceable; } } /** * Custom deserializer that converts a JSON array of strings into a comma-separated string. * *

Example JSON: {@code ["tag1", "tag2", "tag3"]} → {@code "tag1, tag2, tag3"} * *

This tests the ability to use {@link JsonParser#getArray()} to access * JSON structure objects during deserialization. */ public static class TagsDeserializer implements JsonbDeserializer { @Override public String deserialize(JsonParser jp, DeserializationContext dc, Type type) { final JsonValue v = jp.getArray(); if (v instanceof JsonArray) { JsonArray arr = (JsonArray) v; return arr.stream() .filter(JsonString.class::isInstance) .map(JsonString.class::cast) .map(JsonString::getString) .collect(Collectors.joining(", ")); } return null; } } /** * Custom deserializer that handles polymorphic deserialization of {@link Referenceable} objects. * *

Supports two JSON representations: *

    *
  • String value → {@link IRIReference} (e.g., {@code "http://example.com"})
  • *
  • Object value → {@link Reference} (e.g., {@code {"description": "..."}})
  • *
* *

This tests the ability to: *

    *
  • Use {@link JsonParser#getValue()} to access JSON structure objects
  • *
  • Recursively deserialize nested objects using {@link DeserializationContext#deserialize}
  • *
  • Create new parsers from JSON-P structure objects
  • *
*/ public static class ReferenceableDeserializer implements JsonbDeserializer { @Override public Referenceable deserialize(JsonParser jp, DeserializationContext dc, Type type) { final JsonValue v = jp.getValue(); if (v instanceof JsonString) { JsonString str = (JsonString) v; return new IRIReference(str.getString()); } if (v instanceof JsonObject) { JsonObject obj = (JsonObject) v; return dc.deserialize(Reference.class, Json.createParserFactory(Collections.emptyMap()) .createParser(obj)); } return null; } } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.YassonJsonb; import jakarta.json.JsonArray; import jakarta.json.JsonNumber; import jakarta.json.JsonObject; import jakarta.json.JsonString; import jakarta.json.JsonStructure; import jakarta.json.JsonValue; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; public class JsonGeneratorToStructureAdapterTest { @Test public void testSimplePojo() { Pojo pojo = new Pojo(); pojo.setBigDecimalProperty(BigDecimal.TEN); pojo.setLongProperty(10L); pojo.setStringProperty("String value"); JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo); assertEquals("String value", getString(result.get("stringProperty"))); JsonValue bigDecimalProperty = result.get("bigDecimalProperty"); assertEquals(JsonValue.ValueType.NUMBER, bigDecimalProperty.getValueType()); assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue()); JsonValue longProperty = result.get("longProperty"); assertEquals(JsonValue.ValueType.NUMBER, longProperty.getValueType()); assertEquals(10L, ((JsonNumber) longProperty).longValueExact()); } @Test public void testInnerObjects() { Pojo pojo = new Pojo(); pojo.setBigDecimalProperty(BigDecimal.TEN); pojo.setLongProperty(10L); pojo.setStringProperty("String value"); pojo.setInner(new InnerPojo()); pojo.getInner().setInnerFirst("First"); pojo.getInner().setInnerSecond("Second"); JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo, Pojo.class); assertEquals("String value", getString(result.get("stringProperty"))); JsonValue bigDecimalProperty = result.get("bigDecimalProperty"); assertTrue(bigDecimalProperty instanceof JsonNumber); assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue()); JsonValue longProperty = result.get("longProperty"); assertTrue(longProperty instanceof JsonNumber); assertEquals(10L, ((JsonNumber) longProperty).longValueExact()); JsonValue inner = result.get("inner"); assertEquals(JsonValue.ValueType.OBJECT, inner.getValueType()); assertEquals("First", ((JsonObject)inner).getString("innerFirst")); assertEquals("Second", ((JsonObject)inner).getString("innerSecond")); } @Test public void testSimpleJsonArray() { List objList = new ArrayList<>(); objList.add("First"); objList.add(10L); objList.add(BigDecimal.ONE); objList.add(Boolean.TRUE); objList.add(null); JsonArray result = (JsonArray) yassonJsonb.toJsonStructure(objList); assertEquals("First", result.getString(0)); assertEquals(10L, result.getJsonNumber(1).longValueExact()); assertEquals(BigDecimal.ONE, result.getJsonNumber(2).bigDecimalValue()); assertEquals(Boolean.TRUE, result.getBoolean(3)); assertEquals(JsonValue.ValueType.NULL, result.get(4).getValueType()); } @Test public void testJsonArrayInJsonObject() { Pojo pojo = new Pojo(); pojo.setStrings(new ArrayList<>()); pojo.setBigDecimals(new ArrayList<>()); pojo.setBooleans(new ArrayList<>()); pojo.getStrings().add("First"); pojo.getBigDecimals().add(BigDecimal.TEN); pojo.getBooleans().add(Boolean.TRUE); JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo); assertEquals(JsonValue.ValueType.ARRAY, result.get("strings").getValueType()); assertEquals(JsonValue.ValueType.ARRAY, result.get("bigDecimals").getValueType()); assertEquals(JsonValue.ValueType.ARRAY, result.get("booleans").getValueType()); assertEquals("First", result.getJsonArray("strings").getString(0)); assertEquals(BigDecimal.TEN, result.getJsonArray("bigDecimals").getJsonNumber(0).bigDecimalValue()); assertEquals(Boolean.TRUE, result.getJsonArray("booleans").getBoolean(0)); } @Test public void testNestedJsonArrays() { List> outer = new ArrayList<>(); List inner = new ArrayList<>(); inner.add("First"); inner.add(10L); inner.add(BigDecimal.ONE); inner.add(Boolean.TRUE); inner.add(null); outer.add(inner); JsonArray result = (JsonArray) yassonJsonb.toJsonStructure(outer); assertEquals(JsonValue.ValueType.ARRAY, result.get(0).getValueType()); JsonArray resultInner = result.getJsonArray(0); assertEquals("First", resultInner.getString(0)); assertEquals(10L, resultInner.getJsonNumber(1).longValueExact()); assertEquals(BigDecimal.ONE, resultInner.getJsonNumber(2).bigDecimalValue()); assertEquals(Boolean.TRUE, resultInner.getBoolean(3)); assertEquals(JsonValue.ValueType.NULL, resultInner.get(4).getValueType()); } @Test public void testCustomJsonbSerializer() { Pojo pojo = new Pojo(); pojo.setInner(new InnerPojo()); pojo.getInner().setInnerFirst("First value"); pojo.getInner().setInnerSecond("Second value"); YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withSerializers(new InnerPojoSerializer())); JsonStructure result = jsonb.toJsonStructure(pojo); assertEquals(JsonValue.ValueType.OBJECT, result.getValueType()); assertEquals(JsonValue.ValueType.OBJECT, ((JsonObject) result).get("inner").getValueType()); JsonObject inner = (JsonObject) ((JsonObject) result).get("inner"); assertEquals(JsonValue.ValueType.STRING, inner.get("first").getValueType()); assertEquals("First value", ((JsonString) inner.get("first")).getString()); assertEquals(JsonValue.ValueType.STRING, inner.get("second").getValueType()); assertEquals("Second value", ((JsonString) inner.get("second")).getString()); } private static String getString(JsonValue value) { if (value instanceof JsonString) { return ((JsonString) value).getString(); } return value.toString(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2026 Contributors to the Eclipse Foundation. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; import static org.eclipse.yasson.Jsonbs.*; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.YassonJsonb; import jakarta.json.JsonArray; import jakarta.json.JsonArrayBuilder; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.spi.JsonProvider; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; public class JsonStructureToParserAdapterTest { private final JsonProvider jsonProvider = JsonProvider.provider(); @Test public void testBasicJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); objectBuilder.add("longProperty", 10L); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertEquals("value 1", result.getStringProperty()); assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty()); assertEquals(Long.valueOf(10), result.getLongProperty()); } @Test public void testNullValues() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.addNull("stringProperty"); objectBuilder.addNull("bigDecimalProperty"); objectBuilder.add("longProperty", 10L); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertNull(result.getStringProperty()); assertNull(result.getBigDecimalProperty()); assertEquals(Long.valueOf(10), result.getLongProperty()); } @Test public void testInnerJsonObjectWrappedWithProperties() { JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("innerFirst", "Inner value 1"); innerBuilder.add("innerSecond", "Inner value 2"); JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("inner", innerBuilder.build()); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); objectBuilder.add("longProperty", 10L); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertEquals("value 1", result.getStringProperty()); assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty()); assertEquals(Long.valueOf(10), result.getLongProperty()); assertEquals("Inner value 1", result.getInner().getInnerFirst()); assertEquals("Inner value 2", result.getInner().getInnerSecond()); } @Test public void testInnerJsonObjectAtEndProperty() { JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("innerFirst", "Inner value 1"); innerBuilder.add("innerSecond", "Inner value 2"); JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); objectBuilder.add("longProperty", 10L); objectBuilder.add("inner", innerBuilder.build()); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertEquals("value 1", result.getStringProperty()); assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty()); assertEquals(Long.valueOf(10), result.getLongProperty()); assertEquals("Inner value 1", result.getInner().getInnerFirst()); assertEquals("Inner value 2", result.getInner().getInnerSecond()); } @Test public void testEmptyJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertNull(result.getStringProperty()); assertNull(result.getBigDecimalProperty()); assertNull(result.getLongProperty()); } @Test public void testEmptyInnerJsonObject() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); JsonObject innerObject = innerBuilder.build(); objectBuilder.add("inner", innerObject); JsonObject jsonObject = objectBuilder.build(); Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertNull(result.getStringProperty()); assertNull(result.getBigDecimalProperty()); assertNull(result.getLongProperty()); assertNotNull(result.getInner()); assertNull(result.getInner().getInnerFirst()); assertNull(result.getInner().getInnerSecond()); } @Test public void testSimpleArray() { JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); arrayBuilder.add(BigDecimal.TEN).add("String value").addNull(); JsonArray jsonArray = arrayBuilder.build(); List result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); assertEquals(3, result.size()); assertEquals(BigDecimal.TEN, result.get(0)); assertEquals("String value", result.get(1)); assertNull(result.get(2)); } @Test public void testArraysInsideObject() { JsonArrayBuilder bigDecBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder strBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder blnBuilder = jsonProvider.createArrayBuilder(); bigDecBuilder.add(BigDecimal.TEN); strBuilder.add("String value 1"); blnBuilder.add(Boolean.TRUE); JsonObjectBuilder pojoBuilder = jsonProvider.createObjectBuilder(); pojoBuilder.add("strings", strBuilder.build()); pojoBuilder.add("bigDecimals", bigDecBuilder.build()); pojoBuilder.add("booleans", blnBuilder.build()); JsonObject jsonObject = pojoBuilder.build(); Pojo pojo = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class); assertEquals(1, pojo.getBigDecimals().size()); assertEquals(1, pojo.getStrings().size()); assertEquals(1, pojo.getBooleans().size()); } @Test public void testNestedArrays() { JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); JsonArrayBuilder innerArrBuilder = jsonProvider.createArrayBuilder(); innerArrBuilder.add("first").add("second"); arrayBuilder.add(BigDecimal.TEN); arrayBuilder.add(innerArrBuilder.build()); JsonArray jsonArray = arrayBuilder.build(); ArrayList result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class); assertEquals(2, result.size()); assertEquals(BigDecimal.TEN, result.get(0)); assertTrue(result.get(1) instanceof List); List inner = (List) result.get(1); assertEquals(2, inner.size()); assertEquals("first", inner.get(0)); assertEquals("second", inner.get(1)); } @Test public void testObjectsNestedInArrays() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); objectBuilder.add("longProperty", 10L); JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder(); innerArrayBuilder.add("String value 1"); objectBuilder.add("strings", innerArrayBuilder.build()); JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); arrayBuilder.add(objectBuilder.build()); JsonArray rootArray = arrayBuilder.build(); List result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken>(){}.getType()); assertTrue(result.get(0) instanceof Pojo); Pojo pojo = (Pojo) result.get(0); assertNotNull(pojo); assertEquals("value 1", pojo.getStringProperty()); assertEquals(new BigDecimal("1.1"), pojo.getBigDecimalProperty()); assertEquals(Long.valueOf(10), pojo.getLongProperty()); assertNotNull(pojo.getStrings()); assertEquals(1, pojo.getStrings().size()); assertEquals("String value 1", pojo.getStrings().get(0)); } @Test public void testObjectsNestedInArraysRaw() { JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder(); objectBuilder.add("stringProperty", "value 1"); objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1")); objectBuilder.add("longProperty", 10L); JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder(); innerArrayBuilder.add("String value 1"); objectBuilder.add("strings", innerArrayBuilder.build()); JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder(); arrayBuilder.add(10L); arrayBuilder.add(objectBuilder.build()); arrayBuilder.add("10"); JsonArray rootArray = arrayBuilder.build(); List result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken>(){}.getType()); assertEquals(new BigDecimal("10"), result.get(0)); assertTrue(result.get(1) instanceof Map); Map pojo = (Map) result.get(1); assertNotNull(pojo); assertEquals("value 1", pojo.get("stringProperty")); assertEquals(new BigDecimal("1.1"), pojo.get("bigDecimalProperty")); assertEquals(new BigDecimal(10), pojo.get("longProperty")); assertTrue(pojo.get("strings") instanceof List); List strings = (List) pojo.get("strings"); assertNotNull(strings); assertEquals(1, strings.size()); assertEquals("String value 1", strings.get(0)); } @Test public void testCustomJsonbDeserializer() { JsonObjectBuilder outerBuilder = jsonProvider.createObjectBuilder(); JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder(); innerBuilder.add("first", "String value 1"); innerBuilder.add("second", "String value 2"); outerBuilder.add("inner", innerBuilder.build()); JsonObject object = outerBuilder.build(); YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withDeserializers(new InnerPojoDeserializer())); Pojo result = jsonb.fromJsonStructure(object, Pojo.class); assertNotNull(result.getInner()); assertEquals("String value 1", result.getInner().getInnerFirst()); assertEquals("String value 2", result.getInner().getInnerSecond()); } @Test public void testGetValue() { final String json = """ { "type": "Location", "reference": "dummy reference" } """; Jsonb jsonb = JsonbBuilder.create(); Issue673.LocationInterface result = jsonb.fromJson(json, Issue673.LocationInterface.class); assertNotNull(result); assertTrue(result instanceof Issue673.Location); Issue673.Location location = (Issue673.Location) result; Issue673.Referenceable refAble = location.getReference(); assertNotNull(refAble); assertFalse(refAble instanceof Issue673.Reference); assertTrue(refAble instanceof Issue673.IRIReference); Issue673.IRIReference ref = (Issue673.IRIReference) refAble; assertEquals("dummy reference", ref.getValue()); } @Test public void testGetArray() { final String json = """ { "type": "Location", "tags": ["test1", "test2"] } """; Jsonb jsonb = JsonbBuilder.create(); Issue673.LocationInterface result = jsonb.fromJson(json, Issue673.LocationInterface.class); assertNotNull(result); assertTrue(result instanceof Issue673.Location); Issue673.Location location = (Issue673.Location) result; String tags = location.getTags(); assertNotNull(tags); assertEquals("test1, test2", tags); } } ================================================ FILE: src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.jsonstructure; import java.math.BigDecimal; import java.util.List; public final class Pojo { private String stringProperty; private InnerPojo inner; private BigDecimal bigDecimalProperty; private Long longProperty; private List strings; private List bigDecimals; private List booleans; public String getStringProperty() { return stringProperty; } public void setStringProperty(String stringProperty) { this.stringProperty = stringProperty; } public BigDecimal getBigDecimalProperty() { return bigDecimalProperty; } public void setBigDecimalProperty(BigDecimal bigDecimalProperty) { this.bigDecimalProperty = bigDecimalProperty; } public InnerPojo getInner() { return inner; } public void setInner(InnerPojo inner) { this.inner = inner; } public Long getLongProperty() { return longProperty; } public void setLongProperty(Long longProperty) { this.longProperty = longProperty; } public List getStrings() { return strings; } public void setStrings(List strings) { this.strings = strings; } public List getBigDecimals() { return bigDecimals; } public void setBigDecimals(List bigDecimals) { this.bigDecimals = bigDecimals; } public List getBooleans() { return booleans; } public void setBooleans(List booleans) { this.booleans = booleans; } } ================================================ FILE: src/test/java/org/eclipse/yasson/logger/JsonbLoggerFormatter.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.logger; import java.util.logging.LogRecord; /** * @author Roman Grigoriadi */ public class JsonbLoggerFormatter extends java.util.logging.Formatter { @Override public String format(LogRecord record) { return String.format("%-8s",record.getLevel()) + " => " + record.getMessage()+"\n"; } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/Car.java ================================================ /* * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; import jakarta.json.bind.annotation.JsonbProperty; public record Car(@JsonbProperty("typeChanged") String type, @JsonbProperty("colorChanged") String color) { } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithCreateNamingStrategyTest.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.config.PropertyNamingStrategy; import org.junit.jupiter.api.Test; import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertEquals; public class CarWithCreateNamingStrategyTest { // camel case is intentional for this test case public record Car(String brandName, String colorName) { @JsonbCreator public Car { requireNonNull(brandName, "brandName"); requireNonNull(colorName, "colorName"); } } @Test public void testRecordJsonbCreatorWithNamingStrategy() { // given final JsonbConfig config = new JsonbConfig() .withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES); final Jsonb jsonb = JsonbBuilder.create(config); var json = """ { "brand_name": "Volkswagen", "color_name": "Piano black" } """; // when final Car car = jsonb.fromJson(json, Car.class); // then assertEquals("Volkswagen", car.brandName()); assertEquals("Piano black", car.colorName()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithCreator.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; public record CarWithCreator(String type, String color) { @JsonbCreator public static CarWithCreator create(@JsonbProperty("type") String type, @JsonbProperty("color") String color) { return new CarWithCreator(type, color); } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithDefaultConstructor.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record CarWithDefaultConstructor(String type, String color) { public CarWithDefaultConstructor() { this("some", "other"); } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithExtraMethod.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record CarWithExtraMethod(String type, String color) { public String type() { return type; } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithGenerics.java ================================================ /* * Copyright (c) 2025 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record CarWithGenerics (String type, T color) { } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructors.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record CarWithMultipleConstructors(String type, String color) { public CarWithMultipleConstructors(String type) { this(type, "red"); } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java ================================================ /* * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; import jakarta.json.bind.annotation.JsonbCreator; import jakarta.json.bind.annotation.JsonbProperty; public record CarWithMultipleConstructorsAndCreator(String type, String color) { @JsonbCreator public CarWithMultipleConstructorsAndCreator(@JsonbProperty("type") String type) { this(type, "red"); } } ================================================ FILE: src/test/java/org/eclipse/yasson/records/CarWithoutAnnotations.java ================================================ /* * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record CarWithoutAnnotations(String type, String color) { } ================================================ FILE: src/test/java/org/eclipse/yasson/records/Color.java ================================================ /* * Copyright (c) 2025 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; public record Color(String name, String code) { } ================================================ FILE: src/test/java/org/eclipse/yasson/records/RecordTest.java ================================================ /* * Copyright (c) 2021, 2025 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.records; import jakarta.json.bind.JsonbException; import org.eclipse.yasson.Jsonbs; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.internal.properties.MessageKeys; import org.eclipse.yasson.internal.properties.Messages; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class RecordTest { @Test public void testRecordProcessing() { Car car = new Car("skoda", "green"); String expected = "{\"colorChanged\":\"green\",\"typeChanged\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertEquals(expected, json); Car deserialized = Jsonbs.defaultJsonb.fromJson(expected, Car.class); assertEquals(car, deserialized); } @Test public void testRecordProcessingWithoutJsonbProperties() { CarWithoutAnnotations car = new CarWithoutAnnotations("skoda", "green"); String expected = "{\"color\":\"green\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertEquals(expected, json); CarWithoutAnnotations deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithoutAnnotations.class); assertEquals(car, deserialized); } @Test public void testRecordProcessingWithExtraMethod() { CarWithExtraMethod car = new CarWithExtraMethod("skoda", "green"); String expected = "{\"color\":\"green\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); CarWithExtraMethod deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithExtraMethod.class); assertThat(deserialized, is(car)); } @Test public void testRecordMultipleConstructors() { CarWithMultipleConstructors car = new CarWithMultipleConstructors("skoda"); String expected = "{\"color\":\"red\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); JsonbException jsonbException = assertThrows(JsonbException.class, () -> Jsonbs.defaultJsonb.fromJson(expected, CarWithMultipleConstructors.class)); String expectedMessage = Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, CarWithMultipleConstructors.class); assertThat(jsonbException.getMessage(), is(expectedMessage)); } @Test public void testRecordMultipleConstructorsWithJsonbCreator() { CarWithMultipleConstructorsAndCreator car = new CarWithMultipleConstructorsAndCreator("skoda"); String expected = "{\"color\":\"red\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); CarWithMultipleConstructorsAndCreator deserialized = Jsonbs.defaultJsonb .fromJson(expected, CarWithMultipleConstructorsAndCreator.class); assertThat(car, is(deserialized)); } @Test public void testRecordJsonbCreator() { CarWithCreator car = new CarWithCreator("skoda", "red"); String expected = "{\"color\":\"red\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); CarWithCreator deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithCreator.class); assertThat(deserialized, is(car)); } @Test public void testRecordWithDefaultConstructor() { CarWithDefaultConstructor car = new CarWithDefaultConstructor("skoda", "red"); String expected = "{\"color\":\"red\",\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); assertThrows(JsonbException.class, () -> Jsonbs.defaultJsonb.fromJson(expected, CarWithDefaultConstructor.class)); } @Test public void testRecordWithGenerics() { CarWithGenerics car = new CarWithGenerics<>("skoda", new Color("green", "#00FF00")); String expected = "{\"color\":{\"code\":\"#00FF00\",\"name\":\"green\"},\"type\":\"skoda\"}"; String json = Jsonbs.defaultJsonb.toJson(car); assertThat(json, is(expected)); CarWithGenerics deserialized = Jsonbs.defaultJsonb .fromJson(expected, new TestTypeToken>() {}.getType()); assertThat(deserialized, is(car)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java ================================================ /* * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers; import org.junit.jupiter.api.*; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; import java.io.StringReader; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.Comparator; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.serializers.model.Pokemon; import org.eclipse.yasson.serializers.model.Trainer; /** * Test various use-cases with {@code Map} serializer and de-serializer which * stores Map.Entries as JSON objects in 1:1 relation. */ public class MapToEntriesArraySerializerTest { /** * Comparator that allows to compare at least some similar Number instances. * Does not work for everything, but it's sufficient for used test cases. */ private static final class NumberComparator implements Comparator { @Override public int compare(Number n1, Number n2) { if (n1 != null && n2 != null) { if (((n1 instanceof Float) || (n1 instanceof Double)) && ((n2 instanceof Float) || (n2 instanceof Double))) { return Double.compare(n1.doubleValue(), n2.doubleValue()); } return Long.compare(n1.longValue(), n2.longValue()); } if (n1 == null) { return n2 == null ? 0 : -1; } else { return 1; } } } /** NumberComparator instance to be used. */ private static final Comparator CMP_NUM = new NumberComparator(); // Verification code for serialization: Covers only use-cases used in jUnit tests. // * Key JsonObject is always mapped to Trainer PoJo. // * Value JsonObject is always mapped to Pokemon Pojo. /** * Verify that source Map value and parsed Map entry JsonObject value are equals. * * @param jentry parsed Map entry as JsonObject * @param source source Map for value verification * @param key Map key used to retrieve value */ private static final void verifyMapValues(JsonObject jentry, Map source, K key) { assertNotNull(jentry); assertNotNull(source); assertNotNull(key); V sourceValue = source.get(key); assertNotNull(sourceValue); switch (jentry.getValue("/value").getValueType()) { // Value contains JSON object: it shall be Pokemon PoJo case OBJECT: JsonObject valueObject = jentry.getJsonObject("value"); Pokemon sourcePoJo = (Pokemon) source.get(key); Pokemon valuePojo = new Pokemon( valueObject.getString("name"), valueObject.getString("type"), valueObject.getInt("cp")); assertEquals(sourcePoJo, valuePojo); break; case STRING: String valueString = jentry.getString("value"); String sourceString = (String) source.get(key); assertEquals(sourceString, valueString); break; case NUMBER: Number valueNumber = jentry.getJsonNumber("value").numberValue(); Number sourceNumber = (Number) source.get(key); // Number comparator shall be used here because values may not be of the same type. assertEquals(0, CMP_NUM.compare(sourceNumber, valueNumber)); break; case TRUE: case FALSE: Boolean valueBool = jentry.getBoolean("value"); Boolean sourceBool = (Boolean) source.get(key); assertEquals(sourceBool, valueBool); break; default: throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected"); } } /** * Verify that source Map value and parsed Map entry JsonObject value are equals. * * @param jentry parsed Map entry as JsonObject * @param sourceEntry source Map entry for value verification */ private static final void verifyMapValues(JsonObject jentry, Map.Entry sourceEntry) { assertNotNull(jentry); assertNotNull(sourceEntry); switch (jentry.getValue("/value").getValueType()) { // Value contains JSON object: it shall be Pokemon PoJo case OBJECT: JsonObject valueObject = jentry.getJsonObject("value"); Pokemon sourcePoJo = (Pokemon) sourceEntry.getValue(); Pokemon valuePojo = new Pokemon( valueObject.getString("name"), valueObject.getString("type"), valueObject.getInt("cp")); assertEquals(sourcePoJo, valuePojo); break; case ARRAY: JsonArray valueArray = jentry.getJsonArray("value"); assertTrue(valueArray.size() > 0); verifyMapArrayValue(jentry, valueArray, sourceEntry); break; case STRING: String valueString = jentry.getString("value"); String sourceString = (String) sourceEntry.getValue(); assertEquals(sourceString, valueString); break; case NUMBER: Number valueNumber = jentry.getJsonNumber("value").numberValue(); Number sourceNumber = (Number) sourceEntry.getValue(); // Number comparator shall be used here because values may not be of the same type. assertEquals(0, CMP_NUM.compare(sourceNumber, valueNumber)); break; case TRUE: case FALSE: Boolean valueBool = jentry.getBoolean("value"); Boolean sourceBool = (Boolean) sourceEntry.getValue(); assertEquals(sourceBool, valueBool); break; default: throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected"); } } /** * Retrieve Map.Entry with matching array key from source Map. * * @param source source Map * @param key array key to search for * @param cmp optional comparator to use for search * @param keys source map key Set used to check whether all keys were processed. Key will be removed from set on successful match * @return Map.Entry matching provided key */ private static final Map.Entry getMapEntryForArrayKey(Map source, K[] key, Comparator cmp, Set keys) { for (Map.Entry entry : source.entrySet()) { K[] sourceKey = entry.getKey(); boolean match = key.length == sourceKey.length; if (match) { for (int i = 0; i < key.length && match; i++) { if (cmp != null) { match = (cmp.compare(key[i], sourceKey[i]) == 0); } else { match = key[i].equals(sourceKey[i]); } } // Matching key is removed from Set for key processing check if (match) { keys.remove(entry.getKey()); return entry; } } } return null; } /** * Verify that source Map array value and parsed map array value are the same. * * @param value parsed value * @param sourceValue source Map value * @param cmp optional comparator to use for verification */ private static final void verifyMapArrayValues(V[] value, V[] sourceValue, Comparator cmp) { assertEquals(sourceValue.length, value.length); for (int i = 0; i < sourceValue.length; i++) { if (cmp != null) { assertTrue(cmp.compare(sourceValue[i], value[i]) == 0); } else { assertEquals(sourceValue[i], value[i]); } } } /** * Build Map key as an array. Get corresponding key from source Map. * * @param jentry Map key parsed as JsonArray * @param sourceEntry source Map */ @SuppressWarnings("unchecked") private static final void verifyMapArrayValue(JsonObject jentry, final JsonArray valueArray, Map.Entry sourceEntry) { int size = valueArray.size(); // All array elements in the tests are of the same type. switch (valueArray.get(0).getValueType()) { case OBJECT: V[] keyPoJo = (V[]) new Pokemon[size]; for (int i = 0; i < size; i++) { JsonObject keyItem = valueArray.getJsonObject(i); keyPoJo[i] = (V) new Pokemon(keyItem.getString("name"), keyItem.getString("type"), keyItem.getInt("cp")); } verifyMapArrayValues(keyPoJo, (V[]) sourceEntry.getValue(), null); break; case STRING: V[] keyString = (V[]) new String[size]; for (int i = 0; i < size; i++) { keyString[i] = (V) valueArray.getString(i); } verifyMapArrayValues(keyString, (V[]) sourceEntry.getValue(), null); break; case NUMBER: V[] keyNumber = (V[]) new Number[size]; for (int i = 0; i < size; i++) { keyNumber[i] = (V) valueArray.getJsonNumber(i).numberValue(); } verifyMapArrayValues(keyNumber, (V[]) sourceEntry.getValue(), (Comparator) CMP_NUM); break; case TRUE: case FALSE: V[] keyBool = (V[]) new Boolean[size]; for (int i = 0; i < size; i++) { keyBool[i] = (V) Boolean.valueOf(valueArray.getBoolean(i)); } verifyMapArrayValues(keyBool, (V[]) sourceEntry.getValue(), null); break; default: throw new IllegalStateException(valueArray.getValueType() + "was not expected"); } } /** * Build Map key as an array. Get corresponding key from source Map. * * @param keyArray Map key parsed as JsonArray * @param source source Map */ @SuppressWarnings("unchecked") private static final void verifyMapArrayKey(JsonObject jentry, final JsonArray keyArray, Map source, Set keys) { int size = keyArray.size(); // All array elements in the tests are of the same type. switch (keyArray.get(0).getValueType()) { case OBJECT: K[] keyPoJo = (K[]) new Trainer[size]; for (int i = 0; i < size; i++) { JsonObject keyItem = keyArray.getJsonObject(i); keyPoJo[i] = (K) new Trainer(keyItem.getString("name"), keyItem.getInt("age")); } Map.Entry entryObject = getMapEntryForArrayKey((Map) source, keyPoJo, null, keys); verifyMapValues(jentry, entryObject); break; case STRING: K[] keyString = (K[]) new String[size]; for (int i = 0; i < size; i++) { keyString[i] = (K) keyArray.getString(i); } Map.Entry entryString = getMapEntryForArrayKey((Map) source, keyString, null, keys); verifyMapValues(jentry, entryString); break; case NUMBER: K[] keyNumber = (K[]) new Number[size]; for (int i = 0; i < size; i++) { keyNumber[i] = (K) keyArray.getJsonNumber(i).numberValue(); } Map.Entry entryNumber = getMapEntryForArrayKey((Map) source, keyNumber, (Comparator) CMP_NUM, keys); verifyMapValues(jentry, entryNumber); break; case TRUE: case FALSE: K[] keyBool = (K[]) new Boolean[size]; for (int i = 0; i < size; i++) { keyBool[i] = (K) Boolean.valueOf(keyArray.getBoolean(i)); } Map.Entry entryBool = getMapEntryForArrayKey((Map) source, keyBool, null, keys); verifyMapValues(jentry, entryBool); break; default: throw new IllegalStateException(keyArray.getValueType() + "was not expected"); } } /** * Verify that serialized Map provided as JsonArray matches source Map. * * @param source source Map * @param array serialized Map parsed and provided as JsonArray */ @SuppressWarnings("unchecked") private static final void verifySerialization(Map source, JsonArray array) { assertEquals(source.size(), array.size()); Set keys = source.keySet(); array.forEach(entry -> { JsonObject jentry = entry.asJsonObject(); //JsonValue key = jentry.getValue("/key"); switch (jentry.getValue("/key").getValueType()) { // Key contains JSON object: it shall be Trainer PoJo case OBJECT: { JsonObject keyValue = jentry.getJsonObject("key"); Trainer keyPoJo = new Trainer(keyValue.getString("name"), keyValue.getInt("age")); verifyMapValues(jentry, source, (K) keyPoJo); keys.remove((K) keyPoJo); } break; case ARRAY: { JsonArray keyArray = jentry.getJsonArray("key"); assertTrue(keyArray.size() > 0); verifyMapArrayKey(jentry, keyArray, source, keys); } break; case STRING: { String keyValue = jentry.getString("key"); verifyMapValues(jentry, source, (K) keyValue); keys.remove((K) keyValue); } break; case NUMBER: { Number keyValue = jentry.getJsonNumber("key").numberValue(); verifyMapValues(jentry, source, (K) keyValue); keys.remove((K) keyValue); } break; case TRUE: case FALSE: { Boolean keyValue = jentry.getBoolean("key"); verifyMapValues(jentry, source, (K) keyValue); keys.remove((K) keyValue); } break; default: throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected"); } }); // Verify that all keys were processed. assertTrue(keys.isEmpty()); } //No longer valid test // /** // * Test serialization of Map with Number keys and String values. // */ // @Test // public void testSerializeNumberStringMapToEntriesArray() { // Map map = new TreeMap<>(CMP_NUM); // Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); // map.put(Integer.valueOf(12), "twelve"); // map.put(Short.valueOf((short)48), "forty eight"); // map.put(Long.valueOf(256), "two hundred fifty-six"); // String json = jsonb.toJson(map); // JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); // verifySerialization(map, jarr); // } /** * Test serialization of Map with PoJo keys and PoJo values. */ @Test public void testSerializePoJoPoJoMapToEntriesArray() { Map map = new HashMap<>(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); map.put(new Trainer("John Smith", 35), new Pokemon("Charmander", "fire", 980)); map.put(new Trainer("Tom Jones", 24), new Pokemon("Caterpie", "bug", 437)); map.put(new Trainer("Peter Wright", 27), new Pokemon("Houndour", "dark", 1234)); map.put(new Trainer("Bob Parker", 19), new Pokemon("Sneasel", "ice", 2051)); String json = jsonb.toJson(map); JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); verifySerialization(map, jarr); } /** * Test serialization of Map with mixed simple keys and mixed simple values. */ @Test public void testSerializeSimpleSimpleMapToEntriesArray() { String expected = "{\"false\":true,\"10\":24,\"Name\":\"John Smith\"}"; Map map = new HashMap<>(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); map.put("Name", "John Smith"); map.put(Integer.valueOf(10), Long.valueOf(24l)); map.put(Boolean.FALSE, Boolean.TRUE); String json = jsonb.toJson(map); assertEquals(expected, json); } /** * Test serialization of Map with arrays of simple values. */ @Test public void testSerializeSimpleArraySimpleArrayMapToEntriesArray() { Map map = new HashMap<>(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); map.put(new String[] {"John", "Smith"}, new String[] {"first name", "second name"}); map.put(new String[] {"Pikachu", "electric"}, new String[] {"pokemon name", "pokemon type"}); map.put( new Trainer[] {new Trainer("Bob", 15), new Trainer("Ash", 12)}, new Pokemon[] {new Pokemon("Charmander", "fire", 1245), new Pokemon("Kyogre", "water", 3056)}); String json = jsonb.toJson(map); JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray(); verifySerialization(map, jarr); } /** * Test de-serialization of Map with various simple values as keys and * values. Map is stored as an JsonArray of map entries represented as * JsonObjects. */ @Test public void testDeSerializePrimitivesMapToEntriesArray() { String jsonString = "[" + " {" + " \"key\": \"first\"," + " \"value\": \"Peter Parker\"" + " }," + " {" + " \"key\": 42," + " \"value\": true" + " }," + " {" + " \"key\": false," + " \"value\": 21" + " }" + "]"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map map = jsonb.fromJson(jsonString, Map.class); assertEquals(3, map.size()); // Make sure that all 3 pokemons were checked. int valueCheck = 0x00; for (Map.Entry entry : map.entrySet()) { if ((entry.getKey() instanceof String) && "first".equals(entry.getKey())) { assertEquals("Peter Parker", entry.getValue()); valueCheck |= 0x01; } if ((entry.getKey() instanceof Number) && entry.getKey().equals(new BigDecimal(42))) { assertEquals(true, entry.getValue()); valueCheck |= 0x02; } if ((entry.getKey() instanceof Boolean) && entry.getKey().equals(false)) { assertEquals(new BigDecimal(21), entry.getValue()); valueCheck |= 0x04; } } if ((valueCheck & 0x01) == 0) { fail("Did not find key \"first\" in the Map"); } if ((valueCheck & 0x02) == 0) { fail("Did not find key 42 in the Map"); } if ((valueCheck & 0x04) == 0) { fail("Did not find key false in the Map"); } } /** * Test de-serialization of Map with various classes instances * as keys. Map is stored as an JsonArray of map entries represented as * JsonObjects. */ @Test public void testDeSerializeStringPoJoMapToEntriesArray() { String jsonString = "[" + " {" + " \"key\": \"Pikachu\"," + " \"value\": {" + " \"name\": \"Pikachu\"," + " \"type\": \"electric\"," + " \"cp\": 456" + " }" + " }," + " {" + " \"key\": \"Squirtle\"," + " \"value\": {" + " \"name\": \"Squirtle\"," + " \"type\": \"water\"," + " \"cp\": 124" + " }" + " }," + " {" + " \"key\": \"Rayquaza\"," + " \"value\": {" + " \"name\": \"Rayquaza\"," + " \"type\": \"dragon\"," + " \"cp\": 3273" + " }" + " }" + "]"; ParameterizedType pt = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[] { String.class, Pokemon.class }; } @Override public Type getRawType() { return Map.class; } @Override public Type getOwnerType() { return null; } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map map = jsonb.fromJson(jsonString, pt); assertEquals(3, map.size()); // Make sure that all 3 pokemons were checked. int valueCheck = 0x00; for (Map.Entry entry : map.entrySet()) { Pokemon pokemon = entry.getValue(); if ("Pikachu".equals(entry.getKey())) { assertEquals("Pikachu", pokemon.name); assertEquals("electric", pokemon.type); assertEquals(456, pokemon.cp); valueCheck |= 0x01; } if ("Squirtle".equals(entry.getKey())) { assertEquals("Squirtle", pokemon.name); assertEquals("water", pokemon.type); assertEquals(124, pokemon.cp); valueCheck |= 0x02; } if ("Rayquaza".equals(entry.getKey())) { assertEquals("Rayquaza", pokemon.name); assertEquals("dragon", pokemon.type); assertEquals(3273, pokemon.cp); valueCheck |= 0x04; } } if ((valueCheck & 0x01) == 0) { fail("Did not find key \"Pikachu\" in the Map"); } if ((valueCheck & 0x02) == 0) { fail("Did not find key \"Squirtle\" in the Map"); } if ((valueCheck & 0x04) == 0) { fail("Did not find key \"Rayquaza\" in the Map"); } } /** * Test de-serialization of Map with various classes instances * as keys. Map is stored as an JsonArray of map entries represented as * JsonObjects. */ @Test public void testDeSerializePoJoPoJoMapToEntriesArray() { String jsonString = "[" + " {" + " \"key\": {" + " \"name\": \"Bob\"," + " \"age\": 12" + " }," + " \"value\": {" + " \"name\": \"Pikachu\"," + " \"type\": \"electric\"," + " \"cp\": 456" + " }" + " }," + " {" + " \"key\": {" + " \"name\": \"Ash\"," + " \"age\": 10" + " }," + " \"value\": {" + " \"name\": \"Squirtle\"," + " \"type\": \"water\"," + " \"cp\": 124" + " }" + " }," + " {" + " \"key\": {" + " \"name\": \"Joe\"," + " \"age\": 15" + " }," + " \"value\": {" + " \"name\": \"Rayquaza\"," + " \"type\": \"dragon\"," + " \"cp\": 3273" + " }" + " }" + "]"; ParameterizedType pt = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[] { Trainer.class, Pokemon.class }; } @Override public Type getRawType() { return Map.class; } @Override public Type getOwnerType() { return null; } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map map = jsonb.fromJson(jsonString, pt); assertEquals(3, map.size()); // Make sure that all 3 pokemons were checked. int valueCheck = 0x00; for (Map.Entry entry : map.entrySet()) { Trainer trainer = entry.getKey(); Pokemon pokemon = entry.getValue(); if ("Bob".equals(trainer.name)) { assertEquals(12, trainer.age); assertEquals("Pikachu", pokemon.name); assertEquals("electric", pokemon.type); assertEquals(456, pokemon.cp); valueCheck |= 0x01; } if ("Ash".equals(trainer.name)) { assertEquals(10, trainer.age); assertEquals("Squirtle", pokemon.name); assertEquals("water", pokemon.type); assertEquals(124, pokemon.cp); valueCheck |= 0x02; } if ("Joe".equals(trainer.name)) { assertEquals(15, trainer.age); assertEquals("Rayquaza", pokemon.name); assertEquals("dragon", pokemon.type); assertEquals(3273, pokemon.cp); valueCheck |= 0x04; } } if ((valueCheck & 0x01) == 0) { fail("Did not find key \"Bob\" in the Map"); } if ((valueCheck & 0x02) == 0) { fail("Did not find key \"Ash\" in the Map"); } if ((valueCheck & 0x04) == 0) { fail("Did not find key \"Joe\" in the Map"); } } /** * Test de-serialization of Map. * Map is stored as an JsonArray of map entries represented as JsonObjects. */ @Test public void testDeSerializeIntegerArrayStringArrayMapToEntriesArray() { String jsonString = "[" + " {" + " \"key\": [1,2]," + " \"value\": [" + " \"Bob\"," + " \"Tom\"" + " ]" + " }," + " {" + " \"key\": [3,4]," + " \"value\": [" + " \"John\"," + " \"Greg\"" + " ]" + " }" + "]"; ParameterizedType pt = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[] { Integer[].class, String[].class }; } @Override public Type getRawType() { return Map.class; } @Override public Type getOwnerType() { return null; } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map map = jsonb.fromJson(jsonString, pt); assertEquals(2, map.size()); // Make sure that all map entries were checked. int valueCheck = 0x00; for (Map.Entry entry : map.entrySet()) { Integer[] key = entry.getKey(); String[] value = entry.getValue(); if (key[0] == 1 && key[1] == 2) { assertEquals("Bob" ,value[0]); assertEquals("Tom" ,value[1]); valueCheck |= 0x01; } if (key[0] == 3 && key[1] == 4) { assertEquals("John" ,value[0]); assertEquals("Greg" ,value[1]); valueCheck |= 0x02; } } if ((valueCheck & 0x01) == 0) { fail("Did not find key [1,2] in the Map"); } if ((valueCheck & 0x02) == 0) { fail("Did not find key [3,4] in the Map"); } } /** * Test de-serialization of Map. * Map is stored as an JsonArray of map entries represented as JsonObjects. */ @Test public void testDeSerializePoJoArrayPoJoArrayMapToEntriesArray() { String jsonString = "[" + " {" + " \"key\": [" + " {" + " \"name\": \"Ash\"," + " \"age\": 12" + " },{" + " \"name\": \"Joe\"," + " \"age\": 14" + " }" + " ]," + " \"value\": [" + " {" + " \"name\": \"Rayquaza\"," + " \"type\": \"dragon\"," + " \"cp\": 3273" + " },{" + " \"name\": \"Tyranitar\"," + " \"type\": \"dark\"," + " \"cp\": 3181" + " }" + " ]" + " },{" + " \"key\": [" + " {" + " \"name\": \"Bob\"," + " \"age\": 13" + " },{" + " \"name\": \"Maggie\"," + " \"age\": 15" + " }" + " ]," + " \"value\": [" + " {" + " \"name\": \"Raikou\"," + " \"type\": \"electric\"," + " \"cp\": 3095" + " },{" + " \"name\": \"Mamoswine\"," + " \"type\": \"ice\"," + " \"cp\": 3055" + " }" + " ]" + " }" + "]"; ParameterizedType pt = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[] { Trainer[].class, Pokemon[].class }; } @Override public Type getRawType() { return Map.class; } @Override public Type getOwnerType() { return null; } }; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map map = jsonb.fromJson(jsonString, pt); assertEquals(2, map.size()); int valueCheck = 0x00; for (Map.Entry entry : map.entrySet()) { Trainer[] key = entry.getKey(); Pokemon[] value = entry.getValue(); if (key[0].name.equals("Ash") && key[1].name.equals("Joe")) { assertEquals(12, key[0].age); assertEquals(14, key[1].age); assertEquals("Rayquaza", value[0].name); assertEquals("dragon", value[0].type); assertEquals(3273, value[0].cp); assertEquals("Tyranitar", value[1].name); assertEquals("dark", value[1].type); assertEquals(3181, value[1].cp); valueCheck |= 0x01; } if (key[0].name.equals("Bob") && key[1].name.equals("Maggie")) { assertEquals(13, key[0].age); assertEquals(15, key[1].age); assertEquals("Raikou", value[0].name); assertEquals("electric", value[0].type); assertEquals(3095, value[0].cp); assertEquals("Mamoswine", value[1].name); assertEquals("ice", value[1].type); assertEquals(3055, value[1].cp); valueCheck |= 0x02; } } if ((valueCheck & 0x01) == 0) { fail("Did not find key with \"Ash\" and \"Joe\" in the Map"); } if ((valueCheck & 0x02) == 0) { fail("Did not find key with \"Bob\" and \"Maggie\" in the Map"); } } public static class LocaleSerializer implements JsonbSerializer { @Override public void serialize(Locale obj, JsonGenerator generator, SerializationContext ctx) { generator.write(obj.toLanguageTag()); } } public static class LocaleDeserializer implements JsonbDeserializer { @Override public Locale deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { return Locale.forLanguageTag(parser.getString()); } } public static class LocalDateSerializer implements JsonbSerializer { private static final DateTimeFormatter SHORT_FORMAT = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); @Override public void serialize(LocalDate obj, JsonGenerator generator, SerializationContext ctx) { generator.write(SHORT_FORMAT.format(obj)); } } public static class LocalDateDeserializer implements JsonbDeserializer { private static final DateTimeFormatter SHORT_FORMAT = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); @Override public LocalDate deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { return LocalDate.parse(parser.getString(), SHORT_FORMAT); } } public static class MapObject { private Map values; public MapObject() { this.values = new HashMap<>(); } public Map getValues() { return values; } public void setValues(Map values) { if (values == null) { throw new IllegalArgumentException("values cannot be null"); } this.values = values; } @Override public boolean equals(Object o) { if (o instanceof MapObject) { MapObject to = (MapObject) o; return values.equals(to.values); } return false; } @Override public int hashCode() { return Objects.hashCode(this.values); } @Override public String toString() { return values.toString(); } } public static class MapObjectLocaleString extends MapObject {}; private void verifyMapObjectLocaleStringSerialization(JsonObject jsonObject, MapObjectLocaleString mapObject) { // Expected serialization is: {"values":[{"key":"lang-tag","value":"string"},...]} assertEquals(1, jsonObject.size()); assertNotNull(jsonObject.get("values")); assertEquals(JsonValue.ValueType.ARRAY, jsonObject.get("values").getValueType()); JsonArray jsonArray = jsonObject.getJsonArray("values"); assertEquals(mapObject.getValues().size(), jsonArray.size()); MapObjectLocaleString resObject = new MapObjectLocaleString(); for (JsonValue jsonValue : jsonArray) { assertEquals(JsonValue.ValueType.OBJECT, jsonValue.getValueType()); JsonObject entry = jsonValue.asJsonObject(); assertEquals(2, entry.size()); assertNotNull(entry.get("key")); assertEquals(JsonValue.ValueType.STRING, entry.get("key").getValueType()); assertNotNull(entry.get("value")); assertEquals(JsonValue.ValueType.STRING, entry.get("value").getValueType()); resObject.getValues().put(Locale.forLanguageTag(entry.getString("key")), entry.getString("value")); } assertEquals(mapObject, resObject); } /** * Test a Locale/String map with custom Locale serializer and deserializer. */ @Test public void testMapLocaleString() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new LocaleSerializer()) .withDeserializers(new LocaleDeserializer())); MapObjectLocaleString mapObject = new MapObjectLocaleString(); mapObject.getValues().put(Locale.US, "us"); mapObject.getValues().put(Locale.ENGLISH, "en"); mapObject.getValues().put(Locale.JAPAN, "jp"); String json = jsonb.toJson(mapObject); JsonObject jsonObject = Json.createReader(new StringReader(json)).read().asJsonObject(); verifyMapObjectLocaleStringSerialization(jsonObject, mapObject); MapObjectLocaleString resObject = jsonb.fromJson(json, MapObjectLocaleString.class); assertEquals(mapObject, resObject); } public static class MapObjectLocalDateString extends MapObject {}; private void verifyMapObjectCustomLocalDateStringSerialization(JsonObject jsonObject, MapObjectLocalDateString mapObject) { // Expected serialization is: {"values":[{"key":"short-local-date","value":"string"},...]} assertEquals(1, jsonObject.size()); assertNotNull(jsonObject.get("values")); assertEquals(JsonValue.ValueType.ARRAY, jsonObject.get("values").getValueType()); JsonArray jsonArray = jsonObject.getJsonArray("values"); assertEquals(mapObject.getValues().size(), jsonArray.size()); MapObjectLocalDateString resObject = new MapObjectLocalDateString(); for (JsonValue jsonValue : jsonArray) { assertEquals(JsonValue.ValueType.OBJECT, jsonValue.getValueType()); JsonObject entry = jsonValue.asJsonObject(); assertEquals(2, entry.size()); assertNotNull(entry.get("key")); assertEquals(JsonValue.ValueType.STRING, entry.get("key").getValueType()); assertNotNull(entry.get("value")); assertEquals(JsonValue.ValueType.STRING, entry.get("value").getValueType()); resObject.getValues().put(LocalDate.parse(entry.getString("key"), DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)), entry.getString("value")); } assertEquals(mapObject, resObject); } /** * Test for issue #663... * Test a LocalDate/String map as member in a custom class, using a custom LocalDate serializer and deserializer, * even though there's a build-in {@link org.eclipse.yasson.internal.serializer.types.TypeSerializers#isSupportedMapKey(Class)} */ @Test public void testMapLocalDateKeyStringValueAsMember() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new LocalDateSerializer()) .withDeserializers(new LocalDateDeserializer())); MapObjectLocalDateString mapObject = new MapObjectLocalDateString(); mapObject.getValues().put(LocalDate.now(), "today"); mapObject.getValues().put(LocalDate.now().plusDays(1), "tomorrow"); String json = jsonb.toJson(mapObject); JsonObject jsonObject = Json.createReader(new StringReader(json)).read().asJsonObject(); verifyMapObjectCustomLocalDateStringSerialization(jsonObject, mapObject); MapObjectLocalDateString resObject = jsonb.fromJson(json, MapObjectLocalDateString.class); assertEquals(mapObject, resObject); // ensure the keys are of type java.time.LocalDate assertThat(resObject.getValues().keySet().iterator().next(), instanceOf(LocalDate.class)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java ================================================ /* * Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import org.junit.jupiter.api.Test; import static org.eclipse.yasson.Assertions.shouldFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigInteger; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * Test various use-cases with {@code Map} serializer which * stores Map as JSON object. */ public class MapToObjectSerializerTest { /** * Enum used as key in maps during tests */ enum TestEnum { ONE, TWO; @Override /** * Force to lower case to check toString is not used during serialization of maps */ public String toString() { return this.name().toLowerCase(); } } /** * MapObject to test different parametrized maps. * @param The map key * @param The map value */ public static class MapObject { private Map values; public MapObject() { this.values = new HashMap<>(); } public Map getValues() { return values; } public void setValues(Map values) { this.values = values; } @Override public boolean equals(Object o) { if (o instanceof MapObject) { MapObject to = (MapObject) o; return values.equals(to.values); } return false; } @Override public int hashCode() { return Objects.hashCode(this.values); } @Override public String toString() { return values.toString(); } } public static class MapObjectIntegerString extends MapObject {}; public static class MapObjectBigIntegerString extends MapObject {}; public static class MapObjectEnumString extends MapObject {}; public static class MapObjectStringString extends MapObject {}; public static class MapObjectBooleanString extends MapObject {}; /** * Test serialization of Map with Number keys and String values. */ @Test public void testSerializeEnumMapToObject() { Map map = new EnumMap<>(TestEnum.class); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true)); map.put(TestEnum.ONE, "value1"); map.put(TestEnum.TWO, "value2"); String json = jsonb.toJson(map); for (TestEnum e : TestEnum.values()) { assertTrue(json.contains(e.name()), "Enumeration not well serialized"); } } /** * Test for Integer/String map. */ @Test public void testIntegerString() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); MapObjectIntegerString mapObject = new MapObjectIntegerString(); mapObject.getValues().put(12, "twelve"); mapObject.getValues().put(48, "forty eight"); mapObject.getValues().put(256, "two hundred fifty-six"); String json = jsonb.toJson(mapObject); MapObjectIntegerString resObject = jsonb.fromJson(json, MapObjectIntegerString.class); assertEquals(mapObject, resObject); } /** * Test for BigInteger/String map. */ @Test public void testBigIntegerString() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); MapObjectBigIntegerString mapObject = new MapObjectBigIntegerString(); mapObject.getValues().put(new BigInteger("12"), "twelve"); mapObject.getValues().put(new BigInteger("48"), "forty eight"); mapObject.getValues().put(new BigInteger("256"), "two hundred fifty-six"); String json = jsonb.toJson(mapObject); MapObjectBigIntegerString resObject = jsonb.fromJson(json, MapObjectBigIntegerString.class); assertEquals(mapObject, resObject); } /** * Test for Enum/String map. */ @Test public void testEnumString() { Jsonb jsonb = JsonbBuilder.create(); MapObjectEnumString mapObject = new MapObjectEnumString(); mapObject.getValues().put(TestEnum.ONE, "one"); mapObject.getValues().put(TestEnum.TWO, "two"); String json = jsonb.toJson(mapObject); MapObjectEnumString resObject = jsonb.fromJson(json, MapObjectEnumString.class); assertEquals(mapObject, resObject); } /** * Test for String/String map. */ @Test public void testStringString() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty("lala", "lala")); MapObjectStringString mapObject = new MapObjectStringString(); mapObject.getValues().put("one", "one"); mapObject.getValues().put("two", "two"); String json = jsonb.toJson(mapObject); MapObjectStringString resObject = jsonb.fromJson(json, MapObjectStringString.class); assertEquals(mapObject, resObject); } /** * Test for a non parametrized map that should use Strings as keys. */ @Test public void testNotParametrizedMap() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); Map mapObject = new HashMap<>(); mapObject.put(12, "twelve"); mapObject.put(48, "forty eight"); mapObject.put(256, "two hundred fifty-six"); String json = jsonb.toJson(mapObject); Map resObject = jsonb.fromJson(json, Map.class); assertEquals(3, resObject.size()); assertTrue(resObject.keySet().iterator().next() instanceof String); } /** * Test for Boolean/String map. This map is not even generated by the * MapToObjectSerializer as a boolean is not managed by that serializer. * But the json string should be deserialized in the same way. */ @Test public void testBooleanStringMapToObjectSerializer() { Jsonb jsonb = JsonbBuilder.create(); String json = "{\"values\":{\"true\":\"TRUE\",\"false\":\"FALSE\"}}"; MapObjectBooleanString resObject = jsonb.fromJson(json, MapObjectBooleanString.class); assertEquals(2, resObject.getValues().size()); assertEquals("TRUE", resObject.getValues().get(true)); assertEquals("FALSE", resObject.getValues().get(false)); } /** * Test for Integer/String map but giving an incorrect integer key. * JsonbException is expected. */ @Test public void testIncorrectTypeMapToObjectSerializer() { Jsonb jsonb = JsonbBuilder.create(); String json = "{\"values\":{\"1\":\"OK\",\"error\":\"KO\"}}"; shouldFail(() -> jsonb.fromJson(json, MapObjectIntegerString.class)); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/SerializersTest.java ================================================ /* * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers; import java.io.StringReader; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.SortedMap; import java.util.TimeZone; import java.util.TreeMap; import jakarta.json.Json; import jakarta.json.JsonObject; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbTypeSerializer; import jakarta.json.bind.config.PropertyOrderStrategy; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import org.eclipse.yasson.TestTypeToken; import org.eclipse.yasson.YassonConfig; import org.eclipse.yasson.internal.model.ReverseTreeMap; import org.eclipse.yasson.serializers.model.AnnotatedGenericWithSerializerType; import org.eclipse.yasson.serializers.model.AnnotatedWithSerializerType; import org.eclipse.yasson.serializers.model.Author; import org.eclipse.yasson.serializers.model.Box; import org.eclipse.yasson.serializers.model.BoxWithAnnotations; import org.eclipse.yasson.serializers.model.Containee; import org.eclipse.yasson.serializers.model.Container; import org.eclipse.yasson.serializers.model.Crate; import org.eclipse.yasson.serializers.model.CrateDeserializer; import org.eclipse.yasson.serializers.model.CrateDeserializerWithConversion; import org.eclipse.yasson.serializers.model.CrateInner; import org.eclipse.yasson.serializers.model.CrateJsonObjectDeserializer; import org.eclipse.yasson.serializers.model.CrateSerializer; import org.eclipse.yasson.serializers.model.CrateSerializerWithConversion; import org.eclipse.yasson.serializers.model.ExplicitJsonbSerializer; import org.eclipse.yasson.serializers.model.GenericPropertyPojo; import org.eclipse.yasson.serializers.model.ImplicitJsonbSerializer; import org.eclipse.yasson.serializers.model.NumberDeserializer; import org.eclipse.yasson.serializers.model.NumberSerializer; import org.eclipse.yasson.serializers.model.RecursiveDeserializer; import org.eclipse.yasson.serializers.model.RecursiveSerializer; import org.eclipse.yasson.serializers.model.SimpleAnnotatedSerializedArrayContainer; import org.eclipse.yasson.serializers.model.SimpleContainer; import org.eclipse.yasson.serializers.model.StringWrapper; import org.eclipse.yasson.serializers.model.SupertypeSerializerPojo; import org.junit.jupiter.api.Test; import static java.util.Collections.singletonMap; import static org.eclipse.yasson.Jsonbs.defaultJsonb; import static org.eclipse.yasson.Jsonbs.nullableJsonb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * @author Roman Grigoriadi */ public class SerializersTest { private static final JsonbSerializer BOX_ARRAY_SERIALIZER_CHAINED = new JsonbSerializer<>() { public void serialize(Box box, JsonGenerator out, SerializationContext ctx) { out.writeStartArray() .write(box.boxStr) .write(box.secondBoxStr) .writeEnd(); } }; private static final JsonbSerializer BOX_ARRAY_SERIALIZER = new JsonbSerializer<>() { public void serialize(Box box, JsonGenerator out, SerializationContext ctx) { out.writeStartArray(); out.write(box.boxStr); out.write(box.secondBoxStr); out.writeEnd(); } }; @Test public void testClassLevelAnnotation() { Crate crate = new Crate(); crate.crateBigDec = BigDecimal.TEN; crate.crateStr = "crateStr"; crate.annotatedType = new AnnotatedWithSerializerType(); crate.annotatedType.value = "abc"; crate.annotatedGenericType = new AnnotatedGenericWithSerializerType<>(); crate.annotatedGenericType.value = new Crate(); crate.annotatedGenericType.value.crateStr = "inside generic"; crate.annotatedTypeOverriddenOnProperty = new AnnotatedWithSerializerType(); crate.annotatedTypeOverriddenOnProperty.value = "def"; String expected = "{\"annotatedGenericType\":{\"generic\":{\"crate_str\":\"inside generic\"}},\"annotatedType\":{\"valueField\":\"replaced value\"},\"annotatedTypeOverriddenOnProperty\":{\"valueField\":\"overridden value\"},\"crateBigDec\":10,\"crate_str\":\"crateStr\"}"; assertEquals(expected, defaultJsonb.toJson(crate)); Crate result = defaultJsonb.fromJson(expected, Crate.class); assertEquals("replaced value", result.annotatedType.value); assertEquals("overridden value", result.annotatedTypeOverriddenOnProperty.value); assertEquals("inside generic", result.annotatedGenericType.value.crateStr); } @Test public void testClassLevelAnnotationOnRoot() { AnnotatedWithSerializerType annotatedType = new AnnotatedWithSerializerType(); annotatedType.value = "abc"; String expected = "{\"valueField\":\"replaced value\"}"; assertEquals(expected, defaultJsonb.toJson(annotatedType)); AnnotatedWithSerializerType result = defaultJsonb.fromJson(expected, AnnotatedWithSerializerType.class); assertEquals("replaced value", result.value); } @Test public void testClassLevelAnnotationOnGenericRoot() { AnnotatedGenericWithSerializerType annotatedType = new AnnotatedGenericWithSerializerType<>(); annotatedType.value = new Crate(); annotatedType.value.crateStr = "inside generic"; String expected = "{\"generic\":{\"crate_str\":\"inside generic\"}}"; assertEquals(expected, defaultJsonb.toJson(annotatedType)); AnnotatedGenericWithSerializerType result = defaultJsonb.fromJson(expected, new AnnotatedGenericWithSerializerType(){}.getClass().getGenericSuperclass()); assertEquals("inside generic", result.value.crateStr); } /** * Tests JSONB deserialization of arbitrary type invoked from a Deserializer. */ @Test public void testDeserializerDeserializationByType() { JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializer()); Jsonb jsonb = JsonbBuilder.create(config); Box box = createPojoWithDates(); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"date\":\"2015-05-14T11:10:01\"},\"secondBoxStr\":\"Second box string\"}"; Box result = jsonb.fromJson(expected, Box.class); //deserialized by deserializationContext.deserialize(Class c) assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); //set by deserializer statically assertEquals(new BigDecimal("123"), result.crate.crateBigDec); assertEquals("abc", result.crate.crateStr); } /** * Tests JSONB serialization of arbitrary type invoked from a Serializer. */ @Test public void testSerializerSerializationOfType() { JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer()); Jsonb jsonb = JsonbBuilder.create(config); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; Box pojo = createPojo(); assertEquals(expected, jsonb.toJson(pojo)); Box result = jsonb.fromJson(expected, Box.class); assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); //result.crate.crateStr is mapped to crate_str by jsonb property assertNull(result.crate.crateStr); assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); } @Test public void testSerializerSerializationOfTypeWithExplicitType() { JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer()); Jsonb jsonb = JsonbBuilder.create(config); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; Box pojo = createPojo(); assertEquals(expected, jsonb.toJson(pojo, Box.class)); Box result = jsonb.fromJson(expected, Box.class); assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); //result.crate.crateStr is mapped to crate_str by jsonb property assertNull(result.crate.crateStr); assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); } /** * Tests jsonb type conversion, including property customization. */ @Test public void testDeserializersUsingConversion() { JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializerWithConversion()); Jsonb jsonb = JsonbBuilder.create(config); String json = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; Box result = jsonb.fromJson(json, Box.class); final Date expected = getExpectedDate(); assertEquals(expected, result.crate.date); assertEquals("Box string", result.boxStr); assertEquals("Second box string", result.secondBoxStr); } @Test public void testCrateJsonObjectDeserializer() { JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()); Jsonb jsonb = JsonbBuilder.create(config); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crateInnerStr\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}"; Box result = jsonb.fromJson(expected, Box.class); assertEquals(new BigDecimal("54321"), result.crate.crateBigDec); assertEquals("REPLACED crate str", result.crate.crateStr); assertEquals("Single inner", result.crate.crateInner.crateInnerStr); assertEquals(BigDecimal.TEN, result.crate.crateInner.crateInnerBigDec); } private static Date getExpectedDate() { return new Calendar.Builder().setDate(2015, 4, 14).setTimeOfDay(11, 10, 1).setTimeZone(TimeZone.getTimeZone("Z")).build().getTime(); } @Test public void testSerializationUsingConversion() { JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializerWithConversion()); Jsonb jsonb = JsonbBuilder.create(config); String json = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; assertEquals(json, jsonb.toJson(createPojoWithDates())); } @Test public void testAnnotations() { BoxWithAnnotations box = new BoxWithAnnotations(); box.boxStr = "Box string"; box.secondBoxStr = "Second box string"; box.crate = new Crate(); box.crate.date = getExpectedDate(); box.crate.crateInner = createCrateInner("Single inner"); box.crate.crateInnerList = new ArrayList<>(); box.crate.crateInnerList.add(createCrateInner("List inner 0")); box.crate.crateInnerList.add(createCrateInner("List inner 1")); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; assertEquals(expected, defaultJsonb.toJson(box)); BoxWithAnnotations result = defaultJsonb.fromJson(expected, BoxWithAnnotations.class); //deserialized by deserializationContext.deserialize(Class c) assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); assertEquals(2L, result.crate.crateInnerList.size()); assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); //set by deserializer statically assertEquals(new BigDecimal("123"), result.crate.crateBigDec); assertEquals("abc", result.crate.crateStr); } @Test public void testAnnotationsOverride() { JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()).withSerializers(new CrateSerializer()); Jsonb jsonb = JsonbBuilder.create(config); BoxWithAnnotations box = new BoxWithAnnotations(); box.boxStr = "Box string"; box.secondBoxStr = "Second box string"; box.crate = new Crate(); box.crate.crateInner = createCrateInner("Single inner"); box.crate.date = getExpectedDate(); box.crate.crateInnerList = new ArrayList<>(); box.crate.crateInnerList.add(createCrateInner("List inner 0")); box.crate.crateInnerList.add(createCrateInner("List inner 1")); String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}"; assertEquals(expected, jsonb.toJson(box)); BoxWithAnnotations result = jsonb.fromJson(expected, BoxWithAnnotations.class); //deserialized by deserializationContext.deserialize(Class c) assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec); assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr); assertEquals(2L, result.crate.crateInnerList.size()); assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr); assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr); //set by deserializer statically assertEquals(new BigDecimal("123"), result.crate.crateBigDec); assertEquals("abc", result.crate.crateStr); } @Test public void testStringField() { StringWrapper pojo = new StringWrapper(); pojo.strField = "abc"; final String result = defaultJsonb.toJson(pojo); assertEquals("{\"strField\":\" abc\"}", result); } @Test public void testContainerSerializer() { SimpleAnnotatedSerializedArrayContainer container = new SimpleAnnotatedSerializedArrayContainer(); SimpleContainer instance1 = new SimpleContainer(); instance1.setInstance("Test String 1"); SimpleContainer instance2 = new SimpleContainer(); instance2.setInstance("Test String 2"); container.setArrayInstance(new SimpleContainer[] {instance1, instance2}); container.setListInstance(new ArrayList<>()); container.getListInstance().add(new SimpleContainer("Test List 1")); container.getListInstance().add(new SimpleContainer("Test List 2")); String jsonString = defaultJsonb.toJson(container); assertEquals("{\"arrayInstance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}],\"listInstance\":[{\"instance\":\"Test List 1\"},{\"instance\":\"Test List 2\"}]}", jsonString); SimpleAnnotatedSerializedArrayContainer unmarshalledObject = defaultJsonb.fromJson("{\"arrayInstance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}],\"listInstance\":[{\"instance\":\"Test List 1\"},{\"instance\":\"Test List 2\"}]}", SimpleAnnotatedSerializedArrayContainer.class); assertEquals("Test String 1", unmarshalledObject.getArrayInstance()[0].getInstance()); assertEquals("Test String 2", unmarshalledObject.getArrayInstance()[1].getInstance()); assertEquals("Test List 1", unmarshalledObject.getListInstance().get(0).getInstance()); assertEquals("Test List 2", unmarshalledObject.getListInstance().get(1).getInstance()); } /** * Tests avoiding StackOverflowError. */ @Test public void testRecursiveSerializer() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new RecursiveSerializer()).withDeserializers(new RecursiveDeserializer())); Box box = new Box(); box.boxStr = "Box to serialize"; try { jsonb.toJson(box); fail(); } catch (JsonbException ex) { } try { jsonb.fromJson("{\"boxStr\":\"Box to deserialize\"}", Box.class); fail(); } catch (StackOverflowError error){ } } @Test public void testAuthor() { Author author = new Author("Sarah", "Connor"); String expected = "{\"firstName\":\"S\",\"lastName\":\"Connor\"}"; String json = defaultJsonb.toJson(author); assertEquals(expected, json); Author result = defaultJsonb.fromJson(expected, Author.class); assertEquals("John", result.getFirstName()); assertEquals("Connor", result.getLastName()); } @Test public void testSupertypeSerializer() { Jsonb jsonb = JsonbBuilder.create( new JsonbConfig().withSerializers(new NumberSerializer()) .withDeserializers(new NumberDeserializer())); SupertypeSerializerPojo pojo = new SupertypeSerializerPojo(); pojo.setNumberInteger(10); pojo.setAnotherNumberInteger(11); assertEquals("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", jsonb.toJson(pojo)); pojo = jsonb.fromJson("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", SupertypeSerializerPojo.class); assertEquals(Integer.valueOf(10), pojo.getNumberInteger()); assertEquals(Integer.valueOf(11), pojo.getAnotherNumberInteger()); } @Test public void testObjectDeserializerWithLexOrderStrategy() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); Object pojo = jsonb.fromJson("{\"first\":{},\"third\":{},\"second\":{\"second\":2,\"first\":1}}", Object.class); assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap"); @SuppressWarnings("unchecked") SortedMap pojoAsMap = (SortedMap) pojo; assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap"); assertEquals("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", jsonb.toJson(pojo)); } @Test public void testObjectDeserializerWithReverseOrderStrategy() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); Object pojo = jsonb.fromJson("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", Object.class); assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap"); @SuppressWarnings("unchecked") SortedMap pojoAsMap = (SortedMap) pojo; assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap"); assertEquals("{\"third\":{},\"second\":{\"second\":2,\"first\":1},\"first\":{}}", jsonb.toJson(pojo)); } @Test public void testObjectDeserializerWithAnyOrNoneOrderStrategy() { String json = "{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}"; // ANY Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY)); Object pojo = jsonb.fromJson(json, Object.class); assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with \"ANY\" strategy"); // none pojo = defaultJsonb.fromJson(json, Object.class); assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with no strategy"); } @Test public void testSortedMapDerializer() { String json = "{\"first\":1,\"third\":3,\"second\":2}"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY)); SortedMap pojo = jsonb.fromJson(json, SortedMap.class); assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with \"ANY\" strategy"); jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL)); pojo = jsonb.fromJson(json, SortedMap.class); assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy"); assertEquals("{\"first\":1,\"second\":2,\"third\":3}", jsonb.toJson(pojo)); jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE)); pojo = jsonb.fromJson(json, SortedMap.class); assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap with no strategy"); assertEquals("{\"third\":3,\"second\":2,\"first\":1}", jsonb.toJson(pojo)); pojo = defaultJsonb.fromJson(json, SortedMap.class); assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy"); assertEquals("{\"first\":1,\"second\":2,\"third\":3}", defaultJsonb.toJson(pojo)); } @Test public void testGenericPropertyPojoSerializer() { GenericPropertyPojo numberPojo = new GenericPropertyPojo<>(); numberPojo.setProperty(10L); GenericPropertyPojo stringPojo = new GenericPropertyPojo<>(); stringPojo.setProperty("String property"); String numResult = defaultJsonb.toJson(numberPojo, new TestTypeToken>(){}.getType()); assertEquals("{\"propertyByUserSerializer\":\"Number value [10]\"}", numResult); String strResult = defaultJsonb.toJson(stringPojo, new TestTypeToken>(){}.getType()); // because GenericPropertyPojo is annotated to use GenericPropertyPojoSerializer, it will always be // used, despite the fact that the runtime type supplied does not match the serializer type assertEquals("{\"propertyByUserSerializer\":\"Number value [String property]\"}", strResult); } @Test public void testSerializeMapWithNulls() { assertEquals("{\"null\":null}", nullableJsonb.toJson(singletonMap(null, null))); assertEquals("{\"key\":null}", nullableJsonb.toJson(singletonMap("key", null))); assertEquals("{\"null\":\"value\"}", nullableJsonb.toJson(singletonMap(null, "value"))); } @Test public void testSerializeMapWithNullsForceArraySerializer() { Jsonb jsonb = JsonbBuilder.create(new YassonConfig() .withForceMapArraySerializerForNullKeys(Boolean.TRUE) .withNullValues(Boolean.TRUE)); assertEquals("[{\"key\":null,\"value\":null}]", jsonb.toJson(singletonMap(null, null))); assertEquals("{\"key\":null}", jsonb.toJson(singletonMap("key", null))); assertEquals("[{\"key\":null,\"value\":\"value\"}]", jsonb.toJson(singletonMap(null, "value"))); } /** * Test serialization of Map with String keys only. * Map shall be stored as a single JsonObject with keys as object properties names. */ @Test public void testSerializeMapToJsonObject() { Map map = new HashMap<>(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()); map.put("name", "John SMith"); map.put("age", 35); map.put("married", true); String json = jsonb.toJson(map); JsonObject jobj = Json.createReader(new StringReader(json)).read().asJsonObject(); assertEquals("John SMith", jobj.getString("name")); assertEquals(35, jobj.getInt("age")); assertEquals(true, jobj.getBoolean("married")); } @Test public void testDeserializeArrayWithAdvancingParserAfterObjectEnd() { String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoDeserializer())); SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); assertEquals(2, result.length); } public class SimplePojoDeserializer implements JsonbDeserializer { @Override public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { //parser.getObject advances the parser to END_OBJECT. JsonObject json = parser.getObject(); SimplePojo simplePojo = new SimplePojo(); simplePojo.setStringProperty(json.getString("stringProperty")); return simplePojo; } } @Test public void testDeserializeArrayWithAdvancingParserAfterObjectEndUsingValue() { String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]"; Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoValueDeserializer())); SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class); assertEquals(2, result.length); } public class SimplePojoValueDeserializer implements JsonbDeserializer { @Override public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { //parser.getValue advances the parser to END_OBJECT in case of object. JsonObject json = parser.getValue().asJsonObject(); SimplePojo simplePojo = new SimplePojo(); simplePojo.setStringProperty(json.getString("stringProperty")); return simplePojo; } } public class SimplePojo { private String stringProperty; public String getStringProperty() { return stringProperty; } public void setStringProperty(String stringProperty) { this.stringProperty = stringProperty; } } private static Box createPojoWithDates() { Date date = getExpectedDate(); Box box = createPojo(); box.crate.date = date; box.crate.crateInner.date = date; return box; } private static Box createPojo() { Box box = new Box(); box.boxStr = "Box string"; box.crate = new Crate(); box.secondBoxStr = "Second box string"; box.crate.crateInner = createCrateInner("Single inner"); box.crate.crateInnerList = new ArrayList<>(); box.crate.crateInnerList.add(createCrateInner("List inner 0")); box.crate.crateInnerList.add(createCrateInner("List inner 1")); return box; } private static CrateInner createCrateInner(String name) { final CrateInner crateInner = new CrateInner(); crateInner.crateInnerStr = name; crateInner.crateInnerBigDec = BigDecimal.TEN; return crateInner; } public static class Foo { } public static class Bar extends Foo { } public static class Baz extends Bar { } public static class FooSerializer implements JsonbSerializer { public void serialize(Foo obj, JsonGenerator generator, SerializationContext ctx) { generator.write("foo"); } } public static class BazSerializer implements JsonbSerializer { public void serialize(Baz obj, JsonGenerator generator, SerializationContext ctx) { generator.write("baz"); } } /** * Test for issue: https://github.com/quarkusio/quarkus/issues/8925 * Ensure that if multiple customizations (serializer, deserializer, or adapter) are applied * for different types in the same class hierarchy, we use the customization for the most * specific type in the class hierarchy. */ @Test public void testSerializerMatching() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new FooSerializer(), new BazSerializer())); assertEquals("\"foo\"", jsonb.toJson(new Foo())); // Since 'Bar' does not have its own serializer, it should use // the next serializer in the tree (FooSerializer) assertEquals("\"foo\"", jsonb.toJson(new Bar())); assertEquals("\"baz\"", jsonb.toJson(new Baz())); } public static interface One { } public static interface Two { } public static interface Three { } public static class OneTwo implements One, Two { } public static class OneTwoThree implements One, Two, Three { } public static class OneSerializer implements JsonbSerializer { public void serialize(One obj, JsonGenerator generator, SerializationContext ctx) { generator.write("one"); } } public static class TwoSerializer implements JsonbSerializer { public void serialize(Two obj, JsonGenerator generator, SerializationContext ctx) { generator.write("two"); } } public static class ThreeSerializer implements JsonbSerializer { public void serialize(Three obj, JsonGenerator generator, SerializationContext ctx) { generator.write("three"); } } @Test public void testSerializerMatchingInterfaces01() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new OneSerializer(), new TwoSerializer(), new ThreeSerializer())); assertEquals("\"one\"", jsonb.toJson(new OneTwo())); assertEquals("\"one\"", jsonb.toJson(new OneTwoThree())); } @Test public void testSerializerMatchingInterfaces02() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() .withSerializers(new ThreeSerializer(), new TwoSerializer())); assertEquals("\"two\"", jsonb.toJson(new OneTwo())); assertEquals("\"two\"", jsonb.toJson(new OneTwoThree())); } public class GenericBean { public T value; @Override public boolean equals(Object obj) { if (obj instanceof GenericBean){ return Objects.equals(GenericBean.class.cast(obj).value, this.value); } return Boolean.FALSE; } } public class GenericBeanSerializer implements JsonbSerializer { private Boolean called = Boolean.FALSE; @Override public void serialize(GenericBean t, JsonGenerator jg, SerializationContext sc) { called = Boolean.TRUE; jg.writeStartObject(); sc.serialize("value", t.value, jg); jg.writeEnd(); } } public class GenericBeanDeserializer implements JsonbDeserializer { private Boolean called = Boolean.FALSE; @Override public GenericBean deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { called = Boolean.TRUE; JsonObject json = parser.getObject(); GenericBean bean = new GenericBean<>(); bean.value = json.getString("value"); return bean; } } @Test public void testCustomDeserializerWithParameterizedType() { GenericBeanSerializer genericBeanSerializer = new GenericBeanSerializer(); GenericBeanDeserializer genericBeanDeserializer = new GenericBeanDeserializer(); Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(genericBeanDeserializer).withSerializers(genericBeanSerializer)); GenericBean bean1 = new GenericBean<>(); bean1.value = "test1"; GenericBean bean2 = new GenericBean<>(); bean2.value = "test2"; GenericBean bean3 = new GenericBean<>(); bean3.value = "test3"; Collection> asList = Arrays.asList(bean1, bean2, bean3); String toJson = jsonb.toJson(asList); assertEquals(toJson, "[{\"value\":\"test1\"},{\"value\":\"test2\"},{\"value\":\"test3\"}]"); assertTrue(genericBeanSerializer.called); List> fromJson = jsonb.fromJson( toJson, new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[]{GenericBean.class}; } @Override public Type getRawType() { return Collection.class; } @Override public Type getOwnerType() { return null; } } ); assertEquals(asList, fromJson); assertTrue(genericBeanDeserializer.called); } @Test public void testImplicitJsonbSerializers() { Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ExplicitJsonbSerializer())); String expected = "{\"value\":\"123\"}"; Box box = new Box(); box.boxStr = "Box"; assertEquals(expected, jsonb.toJson(box)); jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ImplicitJsonbSerializer())); assertEquals(expected, jsonb.toJson(box)); } @Test public void testBoxToArrayChained() { JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_CHAINED); Jsonb jsonb = JsonbBuilder.create(cfg); Box box = new Box(); box.boxStr = "str1"; box.secondBoxStr = "str2"; String expected = "[\"str1\",\"str2\"]"; assertThat(jsonb.toJson(box), is(expected)); } @Test public void testBoxToArray() { JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER); Jsonb jsonb = JsonbBuilder.create(cfg); Box box = new Box(); box.boxStr = "str1"; box.secondBoxStr = "str2"; String expected = "[\"str1\",\"str2\"]"; assertThat(jsonb.toJson(box), is(expected)); } @Test public void testCustomSerializersInContainer(){ Jsonb jsonb = JsonbBuilder.create(); Container expected = new Container(List.of(new Containee("k", "v"))); String expectedJson = jsonb.toJson(expected); System.out.println(expectedJson); assertEquals(expected, jsonb.fromJson(expectedJson, Container.class)); } /** * Test that annotation-based serializers work when property is declared as Object * but the runtime type has @JsonbTypeSerializer annotation. * This is a regression test for issue #689. */ @Test public void testAnnotationBasedSerializerWithObjectTypedProperty() throws Exception { try (Jsonb jsonb = JsonbBuilder.create()) { final ObjectPropertyContainer container = new ObjectPropertyContainer(); final AnnotatedWithSerializerType objectInstance = new AnnotatedWithSerializerType(); objectInstance.value = "test"; container.annotatedAsObject = objectInstance; container.annotatedConcrete = new AnnotatedWithSerializerType(); container.annotatedConcrete.value = "test2"; final String result = jsonb.toJson(container); // Both properties should use the annotation-based serializer final String expected = "{\"annotatedAsObject\":{\"valueField\":\"replaced value\"},\"annotatedConcrete\":{\"valueField\":\"replaced value\"}}"; assertEquals(expected, result); // Deserialization: annotatedConcrete uses annotation-based deserializer // annotatedAsObject is declared as Object so JSON-B creates a HashMap (expected behavior) final ObjectPropertyContainer deserialized = jsonb.fromJson(expected, ObjectPropertyContainer.class); // In the JSON, the type looks like an object and therefore is a map assertInstanceOf(Map.class, deserialized.annotatedAsObject, "Object property deserializes to Map"); final Map map = (Map) deserialized.annotatedAsObject; assertTrue(map.containsKey("valueField")); assertEquals("replaced value", map.get("valueField")); assertEquals("replaced value", deserialized.annotatedConcrete.value); } } /** * Test that field-level and method-level @JsonbTypeSerializer annotations work on Object-typed properties. * This tests existing AnnotationIntrospector code (not runtime discovery). */ @Test public void testFieldAndMethodLevelSerializerOnObjectType() throws Exception { try (Jsonb jsonb = JsonbBuilder.create()) { final ObjectWithAnnotatedFields container = new ObjectWithAnnotatedFields(); container.fieldAnnotated = "test field"; container.setMethodAnnotated("test method"); final String result = jsonb.toJson(container); // Both should use their respective serializers final String expected = "{\"fieldAnnotated\":\"FIELD:test field\",\"methodAnnotated\":\"METHOD:test method\"}"; assertEquals(expected, result); } } public static class ObjectWithAnnotatedFields { @JsonbTypeSerializer(ObjectFieldSerializer.class) public Object fieldAnnotated; private Object methodAnnotated; @JsonbTypeSerializer(ObjectMethodSerializer.class) public Object getMethodAnnotated() { return methodAnnotated; } public void setMethodAnnotated(Object methodAnnotated) { this.methodAnnotated = methodAnnotated; } } public static class ObjectFieldSerializer implements JsonbSerializer { @Override public void serialize(Object obj, JsonGenerator generator, SerializationContext ctx) { generator.write("FIELD:" + obj.toString()); } } public static class ObjectMethodSerializer implements JsonbSerializer { @Override public void serialize(Object obj, JsonGenerator generator, SerializationContext ctx) { generator.write("METHOD:" + obj.toString()); } } public static class ObjectPropertyContainer { public Object annotatedAsObject; // Declared as Object - this was the bug scenario public AnnotatedWithSerializerType annotatedConcrete; // Declared concretely - should always work } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/TypeDeserializerOnContainersTest.java ================================================ /* * Copyright (c) 2025 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.config.BinaryDataStrategy; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests that {@link jakarta.json.bind.annotation.JsonbTypeDeserializer @JsonbTypeDeserializer} annotated types are * properly detected and used when those types are used as elements/values in containers (Maps, Collections, * Arrays, Optionals). * * @author James R. Perkins */ public class TypeDeserializerOnContainersTest { // Test interface with type-level deserializer annotation @JsonbTypeDeserializer(TestInterfaceDeserializer.class) public interface TestInterface { String getValue(); } // Implementation of the test interface public static class TestImpl implements TestInterface { private final String value; public TestImpl(final String value) { this.value = value; } @Override public String getValue() { return value; } } // Custom deserializer for TestInterface public static class TestInterfaceDeserializer implements JsonbDeserializer { @Override public TestInterface deserialize(final JsonParser parser, final DeserializationContext ctx, final Type rtType) { // Parse the JSON object to get the value field Assertions.assertTrue(parser.hasNext(), "Expected the key name"); parser.next(); Assertions.assertTrue(parser.hasNext(), "Expected the value"); parser.next(); final String value = parser.getString(); Assertions.assertTrue(parser.hasNext(), "Expected the end of an object"); parser.next(); return new TestImpl("DESERIALIZED:" + value); } } // Container classes for testing public static class MapContainer { public Map map; public Map questionKeyMap; public Map questionValueMap; } public static class ListContainer { public List list; public List questionList; } public static class ArrayContainer { public TestInterface[] array; } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public static class OptionalContainer { public Optional optional; public Optional questionOptional; } public static class ByteArrayContainer { public byte[] data; } private Jsonb jsonb; @BeforeEach public void createJsonb() { // Create a new Jsonb for each test to avoid type caching jsonb = JsonbBuilder.create(); } @AfterEach public void closeJsonb() throws Exception { if (jsonb != null) { jsonb.close(); } } @Test public void testTypeDeserializerOnMapValues() { final String json = "{\"map\":{\"key1\":{\"value\":\"value1\"},\"key2\":{\"value\":\"value2\"}}, \"questionKeyMap\":{\"qKey1\":\"value1\",\"qKey2\":\"value2\"},\"questionValueMap\":{\"key1\":\"qValue1\",\"key2\":\"qValue2\"}}"; final MapContainer result = jsonb.fromJson(json, MapContainer.class); Assertions.assertNotNull(result.map); Assertions.assertEquals(2, result.map.size(), () -> String.format("Expected two entries got %s", result.map)); Assertions.assertEquals("DESERIALIZED:value1", result.map.get("key1").getValue()); Assertions.assertEquals("DESERIALIZED:value2", result.map.get("key2").getValue()); Assertions.assertNotNull(result.questionKeyMap); Assertions.assertEquals(2, result.questionKeyMap.size(), () -> String.format("Expected two entries got %s", result.questionKeyMap)); Assertions.assertEquals("value1", result.questionKeyMap.get("qKey1")); Assertions.assertEquals("value2", result.questionKeyMap.get("qKey2")); Assertions.assertNotNull(result.questionValueMap); Assertions.assertEquals(2, result.questionValueMap.size(), () -> String.format("Expected two entries got %s", result.questionValueMap)); Assertions.assertEquals("qValue1", result.questionValueMap.get("key1")); Assertions.assertEquals("qValue2", result.questionValueMap.get("key2")); } @Test public void testTypeDeserializerOnListElements() { final String json = "{\"list\":[{\"value\":\"value1\"},{\"value\":\"value2\"}], \"questionList\": [\"value1\", \"value2\"]}"; final ListContainer result = jsonb.fromJson(json, ListContainer.class); Assertions.assertNotNull(result.list); Assertions.assertEquals(2, result.list.size(), () -> String.format("Expected two entries got %s", result.list)); Assertions.assertEquals("DESERIALIZED:value1", result.list.get(0).getValue()); Assertions.assertEquals("DESERIALIZED:value2", result.list.get(1).getValue()); Assertions.assertNotNull(result.questionList); Assertions.assertEquals(2, result.questionList.size(), () -> String.format("Expected two entries got %s", result.questionList)); Assertions.assertEquals("value1", result.questionList.get(0)); Assertions.assertEquals("value2", result.questionList.get(1)); } @Test public void testTypeDeserializerOnArrayElements() { final String json = "{\"array\":[{\"value\":\"value1\"},{\"value\":\"value2\"}]}"; final ArrayContainer result = jsonb.fromJson(json, ArrayContainer.class); Assertions.assertNotNull(result.array); Assertions.assertEquals(2, result.array.length, () -> String.format("Expected two entries got %s", Arrays.toString(result.array))); Assertions.assertEquals("DESERIALIZED:value1", result.array[0].getValue()); Assertions.assertEquals("DESERIALIZED:value2", result.array[1].getValue()); } @Test public void testTypeDeserializerOnOptionalValue() { final String json = "{\"optional\":{\"value\":\"value1\"},\"questionOptional\":\"value2\"}"; final OptionalContainer result = jsonb.fromJson(json, OptionalContainer.class); Assertions.assertNotNull(result.optional); Assertions.assertTrue(result.optional.isPresent(), "Expected value to be present, but the optional was empty."); Assertions.assertEquals("DESERIALIZED:value1", result.optional.get().getValue()); Assertions.assertNotNull(result.questionOptional); Assertions.assertTrue(result.questionOptional.isPresent(), "Expected value to be present, but the optional was empty."); Assertions.assertEquals("value2", result.questionOptional.get()); } @Test public void testTypeDeserializerOnByteArray() { final String json = "{\"data\":[1,2,3,4,5]}"; final ByteArrayContainer result = jsonb.fromJson(json, ByteArrayContainer.class); Assertions.assertNotNull(result.data); Assertions.assertEquals(5, result.data.length); Assertions.assertArrayEquals(new byte[]{1, 2, 3, 4, 5}, result.data); } @Test public void testTypeDeserializerOnByteArrayWithBase64() throws Exception { try (Jsonb base64Jsonb = JsonbBuilder.create(new JsonbConfig() .withBinaryDataStrategy(BinaryDataStrategy.BASE_64))) { // "SGVsbG8=" is "Hello" in base64 final String json = "{\"data\":\"SGVsbG8=\"}"; final ByteArrayContainer result = base64Jsonb.fromJson(json, ByteArrayContainer.class); Assertions.assertNotNull(result.data); Assertions.assertArrayEquals("Hello".getBytes(), result.data); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/TypeSerializerOnContainersTest.java ================================================ /* * Copyright (c) 2025 IBM and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers; import java.io.StringReader; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonReader; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbException; import jakarta.json.bind.annotation.JsonbTypeSerializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests that {@link jakarta.json.bind.annotation.JsonbTypeSerializer @JsonbTypeSerializer} annotated types are * properly detected and used when those types are used as elements/values in containers (Maps, Collections, * Arrays, Optionals). * * @author James R. Perkins */ public class TypeSerializerOnContainersTest { // Test interface with type-level serializer annotation @JsonbTypeSerializer(TestInterfaceSerializer.class) public interface TestInterface { String getValue(); } // Implementation of the test interface public static class TestImpl implements TestInterface { private final String value; public TestImpl(final String value) { this.value = value; } @Override public String getValue() { return value; } } // Custom serializer for TestInterface public static class TestInterfaceSerializer implements JsonbSerializer { @Override public void serialize(final TestInterface obj, final JsonGenerator generator, final SerializationContext ctx) { generator.write("SERIALIZED:" + obj.getValue()); } } // Container classes for testing public static class MapContainer { public final Map map; public final Map questionKeyMap; public final Map questionValueMap; public MapContainer(final Map map, final Map questionKeyMap, final Map questionValueMap) { this.map = map; this.questionKeyMap = questionKeyMap; this.questionValueMap = questionValueMap; } } public static class ListContainer { public final List list; public final List questionList; public ListContainer(final List list, final List questionList) { this.list = list; this.questionList = questionList; } } public static class ArrayContainer { public final TestInterface[] array; public ArrayContainer(TestInterface[] array) { this.array = array; } } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public static class OptionalContainer { public final Optional optional; public final Optional questionOptional; public OptionalContainer(final Optional optional, final Optional questionOptional) { this.optional = optional; this.questionOptional = questionOptional; } } private Jsonb jsonb; @BeforeEach public void createJsonb() { // Create a new Jsonb for each test to avoid type caching jsonb = JsonbBuilder.create(); } @AfterEach public void closeJsonb() throws Exception { if (jsonb != null) { jsonb.close(); } } @Test public void testTypeSerializerOnMapValues() { final MapContainer container = new MapContainer(Map.of( "key1", new TestImpl("value1"), "key2", new TestImpl("value2") ), Map.of("qKey1", "value1", "qKey2", "value2"), Map.of("key1", "qValue1", "key2", "qValue2") ); final JsonObject json = toJsonObject(container); final JsonObject map = json.getJsonObject("map"); final JsonObject questionKeyMap = json.getJsonObject("questionKeyMap"); final JsonObject questionValueMap = json.getJsonObject("questionValueMap"); Supplier errorMessage = () -> String.format("Expected value not found in %s", map); Assertions.assertEquals("SERIALIZED:value1", map.getString("key1"), errorMessage); Assertions.assertEquals("SERIALIZED:value2", map.getString("key2"), errorMessage); errorMessage = () -> String.format("Expected value not found in %s", questionKeyMap); Assertions.assertEquals("value1", questionKeyMap.getString("qKey1"), errorMessage); Assertions.assertEquals("value2", questionKeyMap.getString("qKey2"), errorMessage); errorMessage = () -> String.format("Expected value not found in %s", questionValueMap); Assertions.assertEquals("qValue1", questionValueMap.getString("key1"), errorMessage); Assertions.assertEquals("qValue2", questionValueMap.getString("key2"), errorMessage); } @Test public void testTypeSerializerOnListElements() { final ListContainer container = new ListContainer(List.of( new TestImpl("value1"), new TestImpl("value2") ), List.of("qValue1", "qValue2")); final JsonObject json = toJsonObject(container); final JsonArray list = json.getJsonArray("list"); final JsonArray questionList = json.getJsonArray("questionList"); Supplier errorMessage = () -> String.format("Expected value not found in %s", list); Assertions.assertEquals(2, list.size(), () -> String.format("Expected a size of 2 in %s", list)); Assertions.assertEquals("SERIALIZED:value1", list.getString(0), errorMessage); Assertions.assertEquals("SERIALIZED:value2", list.getString(1), errorMessage); errorMessage = () -> String.format("Expected value not found in %s", questionList); Assertions.assertEquals(2, questionList.size(), () -> String.format("Expected a size of 2 in %s", questionList)); Assertions.assertEquals("qValue1", questionList.getString(0), errorMessage); Assertions.assertEquals("qValue2", questionList.getString(1), errorMessage); } @Test public void testTypeSerializerOnArrayElements() { final ArrayContainer container = new ArrayContainer(new TestInterface[] { new TestImpl("value1"), new TestImpl("value2") }); final String json = jsonb.toJson(container); Assertions.assertEquals("{\"array\":[\"SERIALIZED:value1\",\"SERIALIZED:value2\"]}", json); } @Test public void testTypeSerializerOnOptionalValue() { final OptionalContainer container = new OptionalContainer(Optional.of(new TestImpl("value1")), Optional.of("value2")); final JsonObject json = toJsonObject(container); Assertions.assertEquals("SERIALIZED:value1", json.getString("optional")); Assertions.assertEquals("value2", json.getString("questionOptional")); } private JsonObject toJsonObject(final Object object) throws JsonbException { final String value = jsonb.toJson(object); try ( StringReader reader = new StringReader(value); JsonReader jsonReader = Json.createReader(reader) ) { return jsonReader.readObject(); } } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AbstractJsonbSerializer.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.Json; import jakarta.json.JsonStructure; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * Abstract serializer. */ public class AbstractJsonbSerializer implements JsonbSerializer { @Override public void serialize(T obj, JsonGenerator generator, SerializationContext ctx) { JsonStructure json = Json.createObjectBuilder().add("value", "123").build(); generator.write(json); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerType.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; @JsonbTypeSerializer(AnnotatedGenericWithSerializerTypeSerializer.class) @JsonbTypeDeserializer(AnnotatedGenericWithSerializerTypeDeserializer.class) public class AnnotatedGenericWithSerializerType { public T value; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeDeserializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import static org.junit.jupiter.api.Assertions.*; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import jakarta.json.stream.JsonParser.Event; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class AnnotatedGenericWithSerializerTypeDeserializer implements JsonbDeserializer> { @Override public AnnotatedGenericWithSerializerType deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { AnnotatedGenericWithSerializerType result = new AnnotatedGenericWithSerializerType<>(); assertEquals(Event.KEY_NAME, parser.next()); assertEquals("generic", parser.getString()); result.value = ctx.deserialize(((ParameterizedType) rtType).getActualTypeArguments()[0], parser); return result; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeSerializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class AnnotatedGenericWithSerializerTypeSerializer implements JsonbSerializer> { @Override public void serialize(AnnotatedGenericWithSerializerType obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); ctx.serialize("generic", obj.value, generator); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerType.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; /** * @author Roman Grigoriadi */ @JsonbTypeSerializer(AnnotatedWithSerializerTypeSerializer.class) @JsonbTypeDeserializer(AnnotatedWithSerializerTypeDeserializer.class) public class AnnotatedWithSerializerType { public String value; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeDeserializer.java ================================================ /* * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; /** * @author Roman Grigoriadi */ public class AnnotatedWithSerializerTypeDeserializer implements JsonbDeserializer { /** * Deserialize an object from JSON. * Cursor of JsonParser is at START_OBJECT. * * @param parser Json parser * @param ctx Deserialization context * @param rtType type of returned object * @return deserialized instance */ @Override public AnnotatedWithSerializerType deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { AnnotatedWithSerializerType result = new AnnotatedWithSerializerType(); parser.next(); parser.next(); result.value = parser.getString(); return result; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * @author Roman Grigoriadi */ public class AnnotatedWithSerializerTypeSerializer implements JsonbSerializer { @Override public void serialize(AnnotatedWithSerializerType obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("valueField", "replaced value"); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializerOverride.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * @author Roman Grigoriadi */ public class AnnotatedWithSerializerTypeSerializerOverride implements JsonbSerializer { @Override public void serialize(AnnotatedWithSerializerType obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("valueField", "overridden value"); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Author.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; public class Author { public static final class FirstNameSerializer implements JsonbSerializer { @Override public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) { generator.write(obj.substring(0, 1)); } } public static final class FirstNameDeserializer implements JsonbDeserializer { @Override public String deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { return "John"; } } @JsonbTypeSerializer(FirstNameSerializer.class) @JsonbTypeDeserializer(FirstNameDeserializer.class) private String firstName; private String lastName; public Author() { } public Author(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Box.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; /** * @author Roman Grigoriadi */ public class Box { public String boxStr; public Crate crate; public String secondBoxStr; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/BoxWithAnnotations.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; /** * @author Roman Grigoriadi */ public class BoxWithAnnotations { public String boxStr; @JsonbTypeSerializer(CrateSerializerWithConversion.class) @JsonbTypeDeserializer(CrateDeserializer.class) public Crate crate; public String secondBoxStr; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Containee.java ================================================ /* * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import java.util.Objects; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; @JsonbTypeDeserializer(ContaineeDeserializer.class) @JsonbTypeSerializer(ContaineeSerializer.class) public class Containee { final String key; final String value; public Containee(String key, String value) { this.key = key; this.value = value; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Containee containee = (Containee) o; return Objects.equals(key, containee.key) && Objects.equals(value, containee.value); } @Override public int hashCode() { return Objects.hash(key, value); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/ContaineeDeserializer.java ================================================ /* * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import java.lang.reflect.Type; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; public class ContaineeDeserializer implements JsonbDeserializer { @Override public Containee deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { String key = null; String value = ""; while (parser.hasNext()) { var event = parser.next(); if (event == JsonParser.Event.KEY_NAME && parser.getString().equals("key")) { parser.next(); // move to VALUE key = parser.getString(); } else if (event == JsonParser.Event.KEY_NAME && parser.getString().equals("value")) { parser.next(); // move to VALUE value = parser.getString(); } } assert key != null; return new Containee(key, value); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/ContaineeSerializer.java ================================================ /* * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class ContaineeSerializer implements JsonbSerializer { @Override public void serialize(Containee obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("key", obj.key); generator.write("value", obj.value); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Container.java ================================================ /* * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class Container { private List containees; public Container() { containees = new ArrayList<>(); } public Container(List containees) { this.containees = containees; } public void setContainees(List containees) { this.containees = containees; } public List getContainees() { return containees; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Container container = (Container) o; return Objects.equals(containees, container.containees); } @Override public int hashCode() { return Objects.hash(containees); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Crate.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbTypeSerializer; import java.math.BigDecimal; import java.util.Date; import java.util.List; /** * @author Roman Grigoriadi */ public class Crate { @JsonbProperty("crate_str") public String crateStr; public CrateInner crateInner; public List crateInnerList; public BigDecimal crateBigDec; @JsonbDateFormat("dd.MM.yyy ^ HH:mm:ss") public Date date; public AnnotatedWithSerializerType annotatedType; public AnnotatedGenericWithSerializerType annotatedGenericType; @JsonbTypeSerializer(AnnotatedWithSerializerTypeSerializerOverride.class) public AnnotatedWithSerializerType annotatedTypeOverriddenOnProperty; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import org.eclipse.yasson.TestTypeToken; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; public class CrateDeserializer implements JsonbDeserializer { @Override public Crate deserialize(JsonParser jsonParser, DeserializationContext ctx, Type rtType) { Crate crate = new Crate(); crate.crateStr = "abc"; crate.crateBigDec = new BigDecimal("123"); while (jsonParser.hasNext()) { JsonParser.Event next = jsonParser.next(); if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("crateInner")) { //invokes JSONB processing for a CrateInner as a root type with "shared" instance of JsonParser crate.crateInner = ctx.deserialize(CrateInner.class, jsonParser); continue; } if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("crateInnerList")) { //invokes JSONB processing for a CrateInner as a root type with "shared" instance of JsonParser crate.crateInnerList = ctx.deserialize(new TestTypeToken>(){}.getType(), jsonParser); continue; } if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("date")) { //move to value jsonParser.next(); //don't have context of processing here, no annotation customizations applied. // crate.date = ctx.convertDefault(Date.class, jsonParser.getString()); ctx.deserialize(Date.class, jsonParser); } } return crate; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializerWithConversion.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; import java.util.Date; /** * @author Roman Grigoriadi */ public class CrateDeserializerWithConversion implements JsonbDeserializer { @Override public Crate deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { Crate result = new Crate(); while (parser.hasNext()) { final JsonParser.Event next = parser.next(); if (next.equals(JsonParser.Event.KEY_NAME) && parser.getString().equals("date-converted")) { parser.next(); result.date = ctx.deserialize(Date.class, parser); break; } } return result; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateInner.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbDateFormat; import jakarta.json.bind.annotation.JsonbProperty; import java.math.BigDecimal; import java.util.Date; /** * @author Roman Grigoriadi */ @JsonbDateFormat("dd.MM.yyyy || HH:mm:ss") public class CrateInner { @JsonbProperty("crate_inner_str") public String crateInnerStr; public BigDecimal crateInnerBigDec; public Date date; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateJsonObjectDeserializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.JsonObject; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; /** * @author Roman Grigoriadi */ public class CrateJsonObjectDeserializer implements JsonbDeserializer { /** * Deserialize an object from JSON. * Cursor of JsonParser is at START_OBJECT. * * @param parser Json parser * @param ctx Deserialization context * @param rtType type of returned object * @return deserialized instance */ @Override public Crate deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { JsonObject crateJsonObject = ctx.deserialize(JsonObject.class, parser); Crate crate = new Crate(); crate.crateInner = new CrateInner(); crate.crateInner.crateInnerStr = crateJsonObject.getJsonObject("crateInner").getString("crateInnerStr"); crate.crateInner.crateInnerBigDec = crateJsonObject.getJsonObject("crateInner").getJsonNumber("crateInnerBigDec").bigDecimalValue(); crate.crateStr = crateJsonObject.getString("crateStr"); crate.crateBigDec = crateJsonObject.getJsonNumber("crateBigDec").bigDecimalValue(); return crate; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateSerializer.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import java.math.BigDecimal; /** * @author Roman Grigoriadi */ public class CrateSerializer implements JsonbSerializer { @Override public void serialize(Crate obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("crateStr", "REPLACED crate str"); ctx.serialize("crateInner", obj.crateInner, generator); ctx.serialize("crateInnerList", obj.crateInnerList, generator); generator.write("crateBigDec", new BigDecimal("54321")); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/CrateSerializerWithConversion.java ================================================ /* * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; import java.math.BigDecimal; /** * @author Roman Grigoriadi */ public class CrateSerializerWithConversion extends CrateSerializer { @Override public void serialize(Crate obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.write("crateStr", "REPLACED crate str"); ctx.serialize("crateInner", obj.crateInner, generator); ctx.serialize("crateInnerList", obj.crateInnerList, generator); generator.write("crateBigDec", new BigDecimal("54321")); ctx.serialize("date-converted", obj.date, generator); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/ExplicitJsonbSerializer.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; /** * Explicit serializer. */ public class ExplicitJsonbSerializer extends AbstractJsonbSerializer implements JsonbSerializer { } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojo.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeSerializer; @JsonbTypeSerializer(GenericPropertyPojoSerializer.class) public class GenericPropertyPojo { private T property; public T getProperty() { return property; } public void setProperty(T property) { this.property = property; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojoSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class GenericPropertyPojoSerializer implements JsonbSerializer> { @Override public void serialize(GenericPropertyPojo obj, JsonGenerator generator, SerializationContext ctx) { generator.writeStartObject(); generator.writeKey("propertyByUserSerializer"); generator.write("Number value [" + obj.getProperty() + "]"); generator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/ImplicitJsonbSerializer.java ================================================ /* * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; /** * Implicit serializer. */ public class ImplicitJsonbSerializer extends AbstractJsonbSerializer { } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; public class NumberDeserializer implements JsonbDeserializer { @Override public Number deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { return Integer.valueOf(parser.getString()) - 1; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class NumberSerializer implements JsonbSerializer { @Override public void serialize(Number obj, JsonGenerator generator, SerializationContext ctx) { generator.write(Integer.valueOf(obj.intValue() + 1).toString()); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Pokemon.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import java.util.Objects; /** * Serialization and de-serialization test model: {@code Pokemon}. */ public final class Pokemon { public String name; public String type; public int cp; public Pokemon() { this.name = null; this.type = null; this.cp = -1; } public Pokemon(String name, String type, int cp) { this.name = name; this.type = type; this.cp = cp; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof Pokemon)) { return false; } Pokemon pokemon = (Pokemon)other; return cp == pokemon.cp && Objects.equals(name, pokemon.name) && Objects.equals(type, pokemon.type); } @Override public int hashCode() { return Objects.hash(cp, name, type); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/RecursiveDeserializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; /** * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime. */ public class RecursiveDeserializer implements JsonbDeserializer { @Override public Box deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) { return deserializationContext.deserialize(type, jsonParser); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/RecursiveSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime. */ public class RecursiveSerializer implements JsonbSerializer { @Override public void serialize(Box box, JsonGenerator jsonGenerator, SerializationContext serializationContext) { jsonGenerator.writeStartObject(); serializationContext.serialize("boxFieldName", box, jsonGenerator); jsonGenerator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/SimpleAnnotatedSerializedArrayContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; import java.util.List; public class SimpleAnnotatedSerializedArrayContainer { @JsonbTypeSerializer(SimpleContainerArraySerializer.class) @JsonbTypeDeserializer(SimpleContainerArrayDeserializer.class) private SimpleContainer[] arrayInstance; private List listInstance; public SimpleContainer[] getArrayInstance() { return arrayInstance; } public void setArrayInstance(SimpleContainer[] arrayInstance) { this.arrayInstance = arrayInstance; } public List getListInstance() { return listInstance; } public void setListInstance(List listInstance) { this.listInstance = listInstance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/SimpleContainer.java ================================================ /* * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.serializers.model; public class SimpleContainer { private String instance = "Test"; public SimpleContainer() { } public SimpleContainer(String instance) { this.instance = instance; } public String getInstance() { return instance; } public void setInstance(String instance) { this.instance = instance; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArrayDeserializer.java ================================================ /* * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.DeserializationContext; import jakarta.json.bind.serializer.JsonbDeserializer; import jakarta.json.stream.JsonParser; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; public class SimpleContainerArrayDeserializer implements JsonbDeserializer { public SimpleContainer[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) { List containers = new ArrayList<>(); while (jsonParser.hasNext()) { JsonParser.Event event = jsonParser.next(); if (event == JsonParser.Event.START_OBJECT) { containers.add(deserializationContext.deserialize(new SimpleContainer(){}.getClass().getGenericSuperclass(), jsonParser)); } if (event == JsonParser.Event.END_OBJECT) { break; } } return containers.toArray(new SimpleContainer[0]); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArraySerializer.java ================================================ /* * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ /* * $Id$ */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; public class SimpleContainerArraySerializer implements JsonbSerializer { @Override public void serialize(SimpleContainer[] containers, JsonGenerator jsonGenerator, SerializationContext serializationContext) { jsonGenerator.writeStartArray(); for (SimpleContainer container : containers) { serializationContext.serialize(container, jsonGenerator); } jsonGenerator.writeEnd(); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/StringPaddingSerializer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.serializer.JsonbSerializer; import jakarta.json.bind.serializer.SerializationContext; import jakarta.json.stream.JsonGenerator; /** * @author Roman Grigoriadi */ public class StringPaddingSerializer implements JsonbSerializer { @Override public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) { generator.write(" "+obj); } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/StringWrapper.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeSerializer; /** * @author Roman Grigoriadi */ public class StringWrapper { @JsonbTypeSerializer(StringPaddingSerializer.class) public String strField; } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/SupertypeSerializerPojo.java ================================================ /* * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import jakarta.json.bind.annotation.JsonbTypeDeserializer; import jakarta.json.bind.annotation.JsonbTypeSerializer; public class SupertypeSerializerPojo { @JsonbTypeSerializer(NumberSerializer.class) @JsonbTypeDeserializer(NumberDeserializer.class) private Integer numberInteger; //Serializers bound with JsonbConfig for this one. private Integer anotherNumberInteger; public Integer getNumberInteger() { return numberInteger; } public void setNumberInteger(Integer numberInteger) { this.numberInteger = numberInteger; } public Integer getAnotherNumberInteger() { return anotherNumberInteger; } public void setAnotherNumberInteger(Integer anotherNumberInteger) { this.anotherNumberInteger = anotherNumberInteger; } } ================================================ FILE: src/test/java/org/eclipse/yasson/serializers/model/Trainer.java ================================================ /* * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, * or the Eclipse Distribution License v. 1.0 which is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.yasson.serializers.model; import java.util.Objects; /** * Serialization and de-serialization test model: pokemon {@code Trainer}. */ public final class Trainer { public String name; public int age; public Trainer() { this.name = null; this.age = -1; } public Trainer(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof Trainer)) { return false; } Trainer trainer = (Trainer)other; return age == trainer.age && Objects.equals(name, trainer.name); } @Override public int hashCode() { return Objects.hash(age, name); } } ================================================ FILE: src/test/resources/META-INF/beans.xml ================================================ ================================================ FILE: src/test/resources/jndi.properties ================================================ # # Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # java.naming.factory.initial=org.eclipse.yasson.internal.cdi.MockJndiContextFactory ================================================ FILE: src/test/resources/logging.properties ================================================ # # Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # handlers=java.util.logging.ConsoleHandler .level=INFO java.util.logging.ConsoleHandler.level=INFO java.util.logging.ConsoleHandler.formatter=org.eclipse.yasson.logger.JsonbLoggerFormatter ================================================ FILE: src/test/resources/test.policy ================================================ grant { //needed to be able to read all files including META-INF entries permission "java.io.FilePermission" "<>", "read"; permission "java.lang.reflect.ReflectPermission" "suppressAccessChecks"; permission "java.lang.RuntimePermission" "accessDeclaredMembers"; permission "java.lang.RuntimePermission" "createSecurityManager"; permission java.lang.RuntimePermission "setSecurityManager"; permission "java.lang.RuntimePermission" "getProtectionDomain"; permission "java.lang.RuntimePermission" "getClassLoader"; permission "java.util.PropertyPermission" "*", "write"; permission "java.util.PropertyPermission" "jsonb.creator-parameters-required", "read"; permission "java.util.PropertyPermission" "yasson.time-in-millis-as-a-string", "read"; permission "java.util.PropertyPermission" "jakarta.json.provider", "read"; }; ================================================ FILE: src/test/resources/yasson-messages_cs.properties ================================================ # # Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License v. 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, # or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # processFromJson = Zpracovávám třídu: {0} z jsonu za použití convertoru: {1} processToJson = Zpracovávám třídu: {0} do jsonu za použití convertoru: {1} diacriticsTest=p\u0159\u00edli\u0161,\u017elu\u0165ou\u010dk\u00fd,k\u016f\u0148,\u00fap\u011bl,\u010f\u00e1belsk\u00e9,\u00f3dy ================================================ FILE: yasson-jmh/.gitignore ================================================ target/ ================================================ FILE: yasson-jmh/README.md ================================================ #JMH performance tests for Yasson. Binaries for performance testing should never be uploaded into Maven repository. Use of JMH dependency is approved for testing purposes only as "Works with" CQ type, see: [jmh-core CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=18908), [jmh-generator-annprocess CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=18909) To run tests: - build it with maven - run from commandline For all JMH options available: ``` java -jar target/yasson-jmh.jar -h ``` Run examples: ``` java -jar target/yasson-jmh.jar java -jar target/yasson-jmh.jar -i 3 -t 5 -f 3 -prof stack java -jar target/yasson-jmh.jar ScalarDataTest.testDeserialize ``` ================================================ FILE: yasson-jmh/pom.xml ================================================ 4.0.0 org.eclipse.yasson yasson-jmh 1.0-SNAPSHOT This is a performance testing project for Yasson. It leverages a JMH technology. See https://openjdk.java.net/projects/code-tools/jmh/. 1.21 2.0.2-SNAPSHOT org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} org.eclipse yasson ${yasson.version} maven-deploy-plugin true maven-javadoc-plugin true org.apache.maven.plugins maven-compiler-plugin 11 11 org.apache.maven.plugins maven-shade-plugin 3.2.1 package shade yasson-jmh org.openjdk.jmh.Main *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/CollectionsTest.java ================================================ package org.eclipse.yasson.jmh; import org.eclipse.yasson.jmh.model.CollectionsData; import org.eclipse.yasson.jmh.model.ScalarData; import org.openjdk.jmh.annotations.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.TimeUnit; /** * Tests for collections processing performance. */ @BenchmarkMode(Mode.Throughput) @Timeout(time = 20) @State(Scope.Benchmark) @Warmup(iterations = 3) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class CollectionsTest { private CollectionsData data; private Jsonb jsonb; private String json; @Setup(Level.Trial) public void setUp() { data = new CollectionsData(); data.setListData(new ArrayList<>()); data.setSetData(new HashSet<>()); for (int i = 0; i < 50; i++) { data.getListData().add(new ScalarData("strValue" + i, i)); data.getSetData().add(new ScalarData("strValue" + i, i)); } jsonb = JsonbBuilder.create(); json = "{\"listData\":[{\"integerValue\":0,\"stringValue\":\"strValue0\"},{\"integerValue\":1,\"stringValue\":\"strValue1\"},{\"integerValue\":2,\"stringValue\":\"strValue2\"},{\"integerValue\":3,\"stringValue\":\"strValue3\"},{\"integerValue\":4,\"stringValue\":\"strValue4\"},{\"integerValue\":5,\"stringValue\":\"strValue5\"},{\"integerValue\":6,\"stringValue\":\"strValue6\"},{\"integerValue\":7,\"stringValue\":\"strValue7\"},{\"integerValue\":8,\"stringValue\":\"strValue8\"},{\"integerValue\":9,\"stringValue\":\"strValue9\"},{\"integerValue\":10,\"stringValue\":\"strValue10\"},{\"integerValue\":11,\"stringValue\":\"strValue11\"},{\"integerValue\":12,\"stringValue\":\"strValue12\"},{\"integerValue\":13,\"stringValue\":\"strValue13\"},{\"integerValue\":14,\"stringValue\":\"strValue14\"},{\"integerValue\":15,\"stringValue\":\"strValue15\"},{\"integerValue\":16,\"stringValue\":\"strValue16\"},{\"integerValue\":17,\"stringValue\":\"strValue17\"},{\"integerValue\":18,\"stringValue\":\"strValue18\"},{\"integerValue\":19,\"stringValue\":\"strValue19\"},{\"integerValue\":20,\"stringValue\":\"strValue20\"},{\"integerValue\":21,\"stringValue\":\"strValue21\"},{\"integerValue\":22,\"stringValue\":\"strValue22\"},{\"integerValue\":23,\"stringValue\":\"strValue23\"},{\"integerValue\":24,\"stringValue\":\"strValue24\"},{\"integerValue\":25,\"stringValue\":\"strValue25\"},{\"integerValue\":26,\"stringValue\":\"strValue26\"},{\"integerValue\":27,\"stringValue\":\"strValue27\"},{\"integerValue\":28,\"stringValue\":\"strValue28\"},{\"integerValue\":29,\"stringValue\":\"strValue29\"},{\"integerValue\":30,\"stringValue\":\"strValue30\"},{\"integerValue\":31,\"stringValue\":\"strValue31\"},{\"integerValue\":32,\"stringValue\":\"strValue32\"},{\"integerValue\":33,\"stringValue\":\"strValue33\"},{\"integerValue\":34,\"stringValue\":\"strValue34\"},{\"integerValue\":35,\"stringValue\":\"strValue35\"},{\"integerValue\":36,\"stringValue\":\"strValue36\"},{\"integerValue\":37,\"stringValue\":\"strValue37\"},{\"integerValue\":38,\"stringValue\":\"strValue38\"},{\"integerValue\":39,\"stringValue\":\"strValue39\"},{\"integerValue\":40,\"stringValue\":\"strValue40\"},{\"integerValue\":41,\"stringValue\":\"strValue41\"},{\"integerValue\":42,\"stringValue\":\"strValue42\"},{\"integerValue\":43,\"stringValue\":\"strValue43\"},{\"integerValue\":44,\"stringValue\":\"strValue44\"},{\"integerValue\":45,\"stringValue\":\"strValue45\"},{\"integerValue\":46,\"stringValue\":\"strValue46\"},{\"integerValue\":47,\"stringValue\":\"strValue47\"},{\"integerValue\":48,\"stringValue\":\"strValue48\"},{\"integerValue\":49,\"stringValue\":\"strValue49\"}],\"setData\":[{\"integerValue\":36,\"stringValue\":\"strValue36\"},{\"integerValue\":21,\"stringValue\":\"strValue21\"},{\"integerValue\":38,\"stringValue\":\"strValue38\"},{\"integerValue\":25,\"stringValue\":\"strValue25\"},{\"integerValue\":32,\"stringValue\":\"strValue32\"},{\"integerValue\":9,\"stringValue\":\"strValue9\"},{\"integerValue\":42,\"stringValue\":\"strValue42\"},{\"integerValue\":35,\"stringValue\":\"strValue35\"},{\"integerValue\":6,\"stringValue\":\"strValue6\"},{\"integerValue\":43,\"stringValue\":\"strValue43\"},{\"integerValue\":37,\"stringValue\":\"strValue37\"},{\"integerValue\":27,\"stringValue\":\"strValue27\"},{\"integerValue\":2,\"stringValue\":\"strValue2\"},{\"integerValue\":1,\"stringValue\":\"strValue1\"},{\"integerValue\":24,\"stringValue\":\"strValue24\"},{\"integerValue\":28,\"stringValue\":\"strValue28\"},{\"integerValue\":29,\"stringValue\":\"strValue29\"},{\"integerValue\":23,\"stringValue\":\"strValue23\"},{\"integerValue\":12,\"stringValue\":\"strValue12\"},{\"integerValue\":22,\"stringValue\":\"strValue22\"},{\"integerValue\":49,\"stringValue\":\"strValue49\"},{\"integerValue\":5,\"stringValue\":\"strValue5\"},{\"integerValue\":44,\"stringValue\":\"strValue44\"},{\"integerValue\":41,\"stringValue\":\"strValue41\"},{\"integerValue\":45,\"stringValue\":\"strValue45\"},{\"integerValue\":0,\"stringValue\":\"strValue0\"},{\"integerValue\":18,\"stringValue\":\"strValue18\"},{\"integerValue\":8,\"stringValue\":\"strValue8\"},{\"integerValue\":10,\"stringValue\":\"strValue10\"},{\"integerValue\":33,\"stringValue\":\"strValue33\"},{\"integerValue\":30,\"stringValue\":\"strValue30\"},{\"integerValue\":13,\"stringValue\":\"strValue13\"},{\"integerValue\":16,\"stringValue\":\"strValue16\"},{\"integerValue\":48,\"stringValue\":\"strValue48\"},{\"integerValue\":3,\"stringValue\":\"strValue3\"},{\"integerValue\":31,\"stringValue\":\"strValue31\"},{\"integerValue\":40,\"stringValue\":\"strValue40\"},{\"integerValue\":15,\"stringValue\":\"strValue15\"},{\"integerValue\":39,\"stringValue\":\"strValue39\"},{\"integerValue\":14,\"stringValue\":\"strValue14\"},{\"integerValue\":46,\"stringValue\":\"strValue46\"},{\"integerValue\":34,\"stringValue\":\"strValue34\"},{\"integerValue\":17,\"stringValue\":\"strValue17\"},{\"integerValue\":7,\"stringValue\":\"strValue7\"},{\"integerValue\":20,\"stringValue\":\"strValue20\"},{\"integerValue\":4,\"stringValue\":\"strValue4\"},{\"integerValue\":11,\"stringValue\":\"strValue11\"},{\"integerValue\":19,\"stringValue\":\"strValue19\"},{\"integerValue\":47,\"stringValue\":\"strValue47\"},{\"integerValue\":26,\"stringValue\":\"strValue26\"}]}"; } @Benchmark public String testSerialize() { return jsonb.toJson(data); } @Benchmark public CollectionsData testDeserialize() { return jsonb.fromJson(json, CollectionsData.class); } } ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/ScalarDataTest.java ================================================ package org.eclipse.yasson.jmh; import org.eclipse.yasson.jmh.model.ScalarData; import org.openjdk.jmh.annotations.*; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import java.util.concurrent.TimeUnit; /** * Tests for simple data, which does not have inner object references or collections. */ @BenchmarkMode(Mode.Throughput) @Timeout(time = 20) @State(Scope.Benchmark) @Warmup(iterations = 3) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class ScalarDataTest { private Jsonb jsonb; private ScalarData data; private String json; @Setup(Level.Trial) public void setUp() { this.jsonb = JsonbBuilder.create(); this.data = new ScalarData(); this.json = "{\"integerValue\":10,\"stringValue\":\"Short string\"}"; } @Benchmark public String testSerialize() { return jsonb.toJson(data); } @Benchmark public ScalarData testDeserialize() { return jsonb.fromJson(json, ScalarData.class); } } ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/TenPropertySerializationTest.java ================================================ package org.eclipse.yasson.jmh; import org.eclipse.yasson.jmh.model.TenPropertyData; import org.openjdk.jmh.annotations.*; import jakarta.json.bind.*; import java.util.List; import java.util.concurrent.TimeUnit; /** * Test for serializing a pojo with 10 properties */ @BenchmarkMode(Mode.Throughput) @Timeout(time = 20) @State(Scope.Benchmark) @Warmup(iterations = 3) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class TenPropertySerializationTest { private Jsonb json; private TenPropertyData testData; @Setup public void setup() { json = JsonbBuilder.create(); testData = new TenPropertyData("prop1", "prop2", 42, 3.14, new String[] {"props", "propsss", "psssss"}, List.of(5, 1, 3), "prop7", 800, new int[] {5555, 6666, 7777, 8888}, Long.MAX_VALUE); } @Benchmark public String testSerialize() { return json.toJson(testData); } } ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/CollectionsData.java ================================================ package org.eclipse.yasson.jmh.model; import java.util.List; import java.util.Set; public class CollectionsData { private List listData; private Set setData; public List getListData() { return listData; } public void setListData(List listData) { this.listData = listData; } public Set getSetData() { return setData; } public void setSetData(Set setData) { this.setData = setData; } } ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/ScalarData.java ================================================ package org.eclipse.yasson.jmh.model; public class ScalarData { private String stringValue; private Integer integerValue; public ScalarData(String stringValue, Integer integerValue) { this.stringValue = stringValue; this.integerValue = integerValue; } public ScalarData() { this.stringValue = "Short string"; this.integerValue = 10; } public String getStringValue() { return stringValue; } public void setStringValue(String stringValue) { this.stringValue = stringValue; } public Integer getIntegerValue() { return integerValue; } public void setIntegerValue(Integer integerValue) { this.integerValue = integerValue; } } ================================================ FILE: yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/TenPropertyData.java ================================================ package org.eclipse.yasson.jmh.model; import java.util.*; public class TenPropertyData{ private final String prop1; private final String prop2; private final int prop3; private final double prop4; private final String[] prop5; private final List prop6; private final String prop7; private final int prop8; private final int[] prop9; private final long prop10; public TenPropertyData(String prop1, String prop2, int prop3, double prop4, String[] prop5, List prop6, String prop7, int prop8, int[] prop9, long prop10) { this.prop1 = prop1; this.prop2 = prop2; this.prop3 = prop3; this.prop4 = prop4; this.prop5 = prop5; this.prop6 = prop6; this.prop7 = prop7; this.prop8 = prop8; this.prop9 = prop9; this.prop10 = prop10; } public String getProp1() { return prop1; } public String getProp2() { return prop2; } public int getProp3() { return prop3; } public double getProp4() { return prop4; } public String[] getProp5() { return prop5; } public List getProp6() { return prop6; } public String getProp7() { return prop7; } public int getProp8() { return prop8; } public int[] getProp9() { return prop9; } public long getProp10() { return prop10; } } ================================================ FILE: yasson-tck/.gitignore ================================================ /target/ ================================================ FILE: yasson-tck/pom.xml ================================================ 4.0.0 org.eclipse yasson-tck 1.0.0-SNAPSHOT 3.0.0 3.0.5-SNAPSHOT 3.0.1 2.1.3 11 11 jakarta.json.bind jakarta.json.bind-tck ${jsonb.tck.version} test jakarta.json.bind jakarta.json.bind-api ${jakarta.json.bind.version} provided jakarta.json jakarta.json-api ${jakarta.json.version} provided org.eclipse yasson ${yasson.version} test org.jboss.weld.se weld-se-core 6.0.0.Beta1 test org.jboss.arquillian.junit5 arquillian-junit5-container 1.8.0.Final org.apache.maven.plugins maven-dependency-plugin 3.6.1 copy generate-test-sources copy jakarta.json.bind jakarta.json.bind-api ${jakarta.json.bind.version} jar true ${project.build.directory}/signaturedirectory jakarta.json.bind-api.jar jakarta.json jakarta.json-api ${jakarta.json.version} jar true ${project.build.directory}/signaturedirectory jakarta.json-api.jar maven-surefire-plugin 3.2.5 false true jakarta.json.bind:jakarta.json.bind-tck ${project.build.directory}/jdk-bundle ${project.build.directory}/signaturedirectory/jakarta.json.bind-api.jar:${project.build.directory}/jdk-bundle/java.base:${project.build.directory}/jdk-bundle/java.rmi:${project.build.directory}/jdk-bundle/java.sql:${project.build.directory}/jdk-bundle/java.naming maven-surefire-report-plugin 3.2.5 post-unit-test test report