import test from 'ava' ; import { ObjectMapper } from '../src/databind/ObjectMapper' ; import { JsonProperty } from '../src/decorators/JsonProperty' ; import { JsonClassType } from '../src/decorators/JsonClassType' ; import { JacksonError } from '../src/core/JacksonError' ; import { JsonIncludeType } from '../src/decorators/JsonInclude' ; test ( 'SerializationFeature.SORT_PROPERTIES_ALPHABETICALLY set to true' , t => { class Book { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) category : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; constructor ( id , name , category ) { this . id = id ; this . name = name ; this . category = category ; } } class Writer { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Map , [ String , Book ] ] } ) bookMap : Map < string , Book > = new Map < string , Book > ( ) ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; constructor ( id , name ) { this . id = id ; this . name = name ; } } const book1 = new Book ( 42 , 'Learning TypeScript' , 'Web Development' ) ; const book2 = new Book ( 21 , 'Learning Spring' , 'Java' ) ; const writer = new Writer ( 1 , 'John' ) ; writer . bookMap . set ( 'book 2' , book2 ) ; writer . bookMap . set ( 'book 1' , book1 ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . SORT_PROPERTIES_ALPHABETICALLY = true ; const jsonData = objectMapper . stringify < Writer > ( writer ) ; t . is ( jsonData , '{"bookMap":{"book 2":{"category":"Java","id":21,"name":"Learning Spring"},"book 1":{"category":"Web Development","id":42,"name":"Learning TypeScript"}},"id":1,"name":"John"}' ) ; } ) ; test ( 'SerializationFeature.ORDER_MAP_AND_OBJECT_LITERAL_ENTRIES_BY_KEYS set to true' , t => { class Book { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) category : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; constructor ( id , name , category ) { this . id = id ; this . name = name ; this . category = category ; } } class Writer { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Map , [ String , Book ] ] } ) bookMap : Map < string , Book > = new Map < string , Book > ( ) ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Object , [ String , Book ] ] } ) bookObjLiteral : { [ key ] : Book ; } = { } ; constructor ( id , name ) { this . id = id ; this . name = name ; } } const book1 = new Book ( 42 , 'Learning TypeScript' , 'Web Development' ) ; const book2 = new Book ( 21 , 'Learning Spring' , 'Java' ) ; const writer = new Writer ( 1 , 'John' ) ; writer . bookMap . set ( 'map book 2' , book2 ) ; writer . bookMap . set ( 'map book 1' , book1 ) ; writer . bookObjLiteral = { 'obj literal book 2' : book2 , 'obj literal book 1' : book1 } ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . ORDER_MAP_AND_OBJECT_LITERAL_ENTRIES_BY_KEYS = true ; const jsonData = objectMapper . stringify < Writer > ( writer ) ; t . assert ( jsonData . includes ( ':{"map book 1":' ) ) ; t . assert ( jsonData . includes ( ',"map book 2":' ) ) ; t . assert ( jsonData . includes ( ':{"obj literal book 1":' ) ) ; t . assert ( jsonData . includes ( ',"obj literal book 2":' ) ) ; t . deepEqual ( JSON . parse ( jsonData ) , JSON . parse ( '{"bookMap":{"map book 1":{"id":42,"name":"Learning TypeScript","category":"Web Development"},"map book 2":{"id":21,"name":"Learning Spring","category":"Java"}},"bookObjLiteral":{"obj literal book 1":{"id":42,"name":"Learning TypeScript","category":"Web Development"},"obj literal book 2":{"id":21,"name":"Learning Spring","category":"Java"}},"id":1,"name":"John"}' ) ) ; } ) ; test ( 'SerializationFeature.FAIL_ON_SELF_REFERENCES set to false' , t => { const errFailOnSelfReferences = t . throws < JacksonError > ( ( ) => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ User ] } ) userRef : User ; constructor ( id , firstname , lastname ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; } } const user = new User ( 1 , 'John' , 'Alfa' ) ; user . userRef = user ; const objectMapper = new ObjectMapper ( ) ; objectMapper . stringify < User > ( user ) ; } ) ; t . assert ( errFailOnSelfReferences instanceof JacksonError ) ; } ) ; test ( 'SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS set to true' , t => { class Event { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Map , [ Date , String ] ] } ) infoMap : Map < Date , string > = new Map < Date , string > ( ) ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Object , [ Date , String ] ] } ) infoObjLiteral = { } ; constructor ( name ) { this . name = name ; } } const eventDate = new Date ( 1586993967000 ) ; const event = new Event ( 'Event 1' ) ; event . infoMap . set ( eventDate , 'info map' ) ; event . infoObjLiteral [ eventDate ] = 'info obj literal' ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_DATE_KEYS_AS_TIMESTAMPS = true ; const jsonData = objectMapper . stringify < Event > ( event ) ; t . deepEqual ( JSON . parse ( jsonData ) , JSON . parse ( '{"infoMap":{"1586993967000":"info map"},"infoObjLiteral":{"1586993967000":"info obj literal"},"name":"Event 1"}' ) ) ; } ) ; test ( 'SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL set to true' , t => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ User ] } ) userRef : User ; constructor ( id , firstname , lastname ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; } } const user = new User ( 1 , 'John' , 'Alfa' ) ; user . userRef = user ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . FAIL_ON_SELF_REFERENCES = false ; objectMapper . defaultStringifierContext . features . serialization . WRITE_SELF_REFERENCES_AS_NULL = true ; const jsonData = objectMapper . stringify < User > ( user ) ; t . deepEqual ( JSON . parse ( jsonData ) , JSON . parse ( '{"id":1,"firstname":"John","lastname":"Alfa","userRef":null}' ) ) ; } ) ; test ( 'SerializationFeature.DEFAULT_PROPERTY_INCLUSION set to JsonIncludeType.NON_EMPTY' , t => { class Employee { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) name : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) dept : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) address : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Array , [ String ] ] } ) phones : string [ ] ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Map , [ String , String ] ] } ) otherInfo : Map < string , string > ; constructor ( id , name , dept , address , phones , otherInfo ) { this . id = id ; this . name = name ; this . dept = dept ; this . address = address ; this . phones = phones ; this . otherInfo = otherInfo ; } } const employee = new Employee ( 0 , 'John' , '' , null , [ ] , new Map < string , string > ( ) ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . DEFAULT_PROPERTY_INCLUSION = { value : JsonIncludeType . NON_EMPTY } ; const jsonData = objectMapper . stringify < Employee > ( employee ) ; t . deepEqual ( JSON . parse ( jsonData ) , JSON . parse ( '{"id":0,"name":"John"}' ) ) ; } ) ; test ( 'SerializationFeature.WRITE_DATES_AS_TIMESTAMPS set to false' , t => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Date ] } ) birthday : Date ; constructor ( id , firstname , lastname , birthday ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; this . birthday = birthday ; } } const birthday = new Date ( 1994 , 11 , 14 ) ; const user = new User ( 1 , 'John' , 'Alfa' , birthday ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_DATES_AS_TIMESTAMPS = false ; const jsonData = objectMapper . stringify < User > ( user ) ; const jsonDataParsed = JSON . parse ( jsonData ) ; t . assert ( typeof jsonDataParsed . birthday === 'string' ) ; t . is ( ( new Date ( jsonDataParsed . birthday ) ) . toString ( ) , birthday . toString ( ) ) ; } ) ; test ( 'SerializationFeature.WRITE_NAN_AS_ZERO set to true' , t => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) age : number ; constructor ( id , firstname , lastname , age ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; this . age = age ; } } const user = new User ( 1 , 'John' , 'Alfa' , NaN ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_NAN_AS_ZERO = true ; const jsonData = objectMapper . stringify < User > ( user ) ; t . deepEqual ( JSON . parse ( jsonData ) , JSON . parse ( '{"id":1,"firstname":"John","lastname":"Alfa","age":0}' ) ) ; } ) ; test ( 'Positive Infinity as NUMBER_MAX_VALUE and NUMBER_MAX_SAFE_INTEGER' , t => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) age : number ; constructor ( id , firstname , lastname , age ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; this . age = age ; } } const user = new User ( 1 , 'John' , 'Alfa' , Infinity ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_POSITIVE_INFINITY_AS_NUMBER_MAX_VALUE = true ; let jsonData = objectMapper . stringify < User > ( user ) ; t . assert ( isFinite ( JSON . parse ( jsonData ) . age ) ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_POSITIVE_INFINITY_AS_NUMBER_MAX_VALUE = false ; objectMapper . defaultStringifierContext . features . serialization . WRITE_POSITIVE_INFINITY_AS_NUMBER_MAX_SAFE_INTEGER = true ; jsonData = objectMapper . stringify < User > ( user ) ; t . assert ( isFinite ( JSON . parse ( jsonData ) . age ) ) ; } ) ; test ( 'Negative Infinity as NUMBER_MIN_VALUE and NUMBER_MIN_SAFE_INTEGER' , t => { class User { @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) id : number ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) firstname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ String ] } ) lastname : string ; @ JsonProperty ( ) @ JsonClassType ( { type : ( ) => [ Number ] } ) age : number ; constructor ( id , firstname , lastname , age ) { this . id = id ; this . firstname = firstname ; this . lastname = lastname ; this . age = age ; } } const user = new User ( 1 , 'John' , 'Alfa' , - Infinity ) ; const objectMapper = new ObjectMapper ( ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_NEGATIVE_INFINITY_AS_NUMBER_MIN_VALUE = true ; let jsonData = objectMapper . stringify < User > ( user ) ; t . assert ( isFinite ( JSON . parse ( jsonData ) . age ) ) ; objectMapper . defaultStringifierContext . features . serialization . WRITE_NEGATIVE_INFINITY_AS_NUMBER_MIN_VALUE = false ; objectMapper . defaultStringifierContext . features . serialization . WRITE_NEGATIVE_INFINITY_AS_NUMBER_MIN_SAFE_INTEGER = true ; jsonData = objectMapper . stringify < User > ( user ) ; t . assert ( isFinite ( JSON . parse ( jsonData ) . age ) ) ; } ) ;