@@ -3,6 +3,7 @@ package wu.seal.jsontokotlin
33import org.junit.Before
44import org.junit.Test
55import wu.seal.jsontokotlin.test.TestConfig
6+ import com.google.gson.annotations.SerializedName
67
78class JsonSchemaTest {
89
@@ -61,4 +62,305 @@ class JsonSchemaTest {
6162 val s = json.generateKotlinClassCode(" TestData" )
6263 println (s)
6364 }
65+
66+ @Test
67+ fun testJsonSchemaWithDefs () {
68+ val json = """ {
69+ "${" $" } schema": "http://json-schema.org/draft-2020-12/schema",
70+ "type": "object",
71+ "title": "Payment",
72+ "description": "A payment instruction with transaction details",
73+ "properties": {
74+ "paymentId": {
75+ "type": "string",
76+ "description": "Unique identifier for the payment"
77+ },
78+ "amount": {
79+ "type": "number",
80+ "description": "Payment amount"
81+ },
82+ "currency": {
83+ "type": "string",
84+ "description": "Payment currency code"
85+ },
86+ "transaction": {
87+ "${" $" } ref": "#/${" $" } defs/Transaction"
88+ }
89+ },
90+ "required": ["paymentId", "amount", "currency", "transaction"],
91+ "${" $" } defs": {
92+ "Transaction": {
93+ "type": "object",
94+ "properties": {
95+ "transactionId": {
96+ "type": "string",
97+ "description": "Unique identifier for the transaction"
98+ },
99+ "status": {
100+ "type": "string",
101+ "enum": ["PENDING", "COMPLETED", "FAILED"],
102+ "description": "Current status of the transaction"
103+ },
104+ "details": {
105+ "${" $" } ref": "#/${" $" } defs/TransactionDetails"
106+ }
107+ },
108+ "required": ["transactionId", "status"]
109+ },
110+ "TransactionDetails": {
111+ "type": "object",
112+ "properties": {
113+ "createdAt": {
114+ "type": "string",
115+ "format": "date-time",
116+ "description": "Transaction creation timestamp"
117+ },
118+ "updatedAt": {
119+ "type": "string",
120+ "format": "date-time",
121+ "description": "Transaction last update timestamp"
122+ },
123+ "notes": {
124+ "type": "string",
125+ "description": "Additional notes about the transaction"
126+ }
127+ }
128+ }
129+ }
130+ }""" .trimIndent()
131+
132+ val expected = """ /**
133+ * A payment instruction with transaction details
134+ */
135+ data class Payment(
136+ /**
137+ * Payment amount
138+ */
139+ @SerializedName("amount")
140+ val amount: Double = 0.0,
141+ /**
142+ * Payment currency code
143+ */
144+ @SerializedName("currency")
145+ val currency: String = "",
146+ /**
147+ * Unique identifier for the payment
148+ */
149+ @SerializedName("paymentId")
150+ val paymentId: String = "",
151+ @SerializedName("transaction")
152+ val transaction: Transaction = Transaction()
153+ ) {
154+ data class Transaction(
155+ @SerializedName("details")
156+ val details: TransactionDetails = TransactionDetails(),
157+ /**
158+ * Current status of the transaction
159+ */
160+ @SerializedName("status")
161+ val status: Status = Status(),
162+ /**
163+ * Unique identifier for the transaction
164+ */
165+ @SerializedName("transactionId")
166+ val transactionId: String = ""
167+ ) {
168+ data class TransactionDetails(
169+ /**
170+ * Transaction creation timestamp
171+ */
172+ @SerializedName("createdAt")
173+ val createdAt: java.time.OffsetDateTime = OffsetDateTime(),
174+ /**
175+ * Additional notes about the transaction
176+ */
177+ @SerializedName("notes")
178+ val notes: String = "",
179+ /**
180+ * Transaction last update timestamp
181+ */
182+ @SerializedName("updatedAt")
183+ val updatedAt: java.time.OffsetDateTime = OffsetDateTime()
184+ )
185+
186+ /**
187+ * Current status of the transaction
188+ */
189+ enum class Status(val value: String) {
190+ PENDING("PENDING"),
191+
192+ COMPLETED("COMPLETED"),
193+
194+ FAILED("FAILED");
195+ }
196+ }
197+ }""" .trimIndent()
198+
199+ val result = json.generateKotlinClassCode(" Payment" )
200+ println (result)
201+
202+ // Use assertion to verify that the generated code matches the expected result
203+ assert (result.trim() == expected) { " Generated code does not match expected output" }
204+ }
205+
206+ @Test
207+ fun testComplexJsonSchemaWithDefs () {
208+ // This test simulates the case from the crash report involving #/$defs/CreditTransferTransaction39
209+ val json = """ {
210+ "${" $" } schema": "http://json-schema.org/draft-2020-12/schema",
211+ "title": "PaymentInstruction",
212+ "type": "object",
213+ "properties": {
214+ "msgId": {
215+ "type": "string",
216+ "description": "Point to point reference assigned by the instructing party to unambiguously identify the instruction"
217+ },
218+ "pmtInf": {
219+ "type": "array",
220+ "items": {
221+ "${" $" } ref": "#/${" $" } defs/PaymentInformation"
222+ }
223+ }
224+ },
225+ "required": ["msgId", "pmtInf"],
226+ "${" $" } defs": {
227+ "PaymentInformation": {
228+ "type": "object",
229+ "properties": {
230+ "pmtInfId": {
231+ "type": "string",
232+ "description": "Unique identification assigned by the sending party to unambiguously identify the payment information group"
233+ },
234+ "pmtMtd": {
235+ "type": "string",
236+ "description": "Specifies the means of payment that will be used to move the amount of money"
237+ },
238+ "cdtTrfTxInf": {
239+ "type": "array",
240+ "items": {
241+ "${" $" } ref": "#/${" $" } defs/CreditTransferTransaction"
242+ }
243+ }
244+ },
245+ "required": ["pmtInfId", "pmtMtd"]
246+ },
247+ "CreditTransferTransaction": {
248+ "type": "object",
249+ "properties": {
250+ "pmtId": {
251+ "type": "object",
252+ "properties": {
253+ "endToEndId": {
254+ "type": "string",
255+ "description": "Unique identification assigned by the initiating party to unambiguously identify the transaction"
256+ },
257+ "txId": {
258+ "type": "string",
259+ "description": "Unique identification assigned by the first instructing agent to unambiguously identify the transaction"
260+ }
261+ },
262+ "required": ["endToEndId"]
263+ },
264+ "amt": {
265+ "${" $" } ref": "#/${" $" } defs/ActiveCurrencyAndAmount"
266+ },
267+ "cdtr": {
268+ "${" $" } ref": "#/${" $" } defs/PartyIdentification"
269+ },
270+ "cdtrAcct": {
271+ "${" $" } ref": "#/${" $" } defs/CashAccount"
272+ }
273+ },
274+ "required": ["pmtId", "amt", "cdtr", "cdtrAcct"]
275+ },
276+ "ActiveCurrencyAndAmount": {
277+ "type": "object",
278+ "properties": {
279+ "ccy": {
280+ "type": "string",
281+ "description": "Medium of exchange of value, such as USD or EUR"
282+ },
283+ "value": {
284+ "type": "number",
285+ "description": "Amount of money to be moved between the debtor and creditor"
286+ }
287+ },
288+ "required": ["ccy", "value"]
289+ },
290+ "PartyIdentification": {
291+ "type": "object",
292+ "properties": {
293+ "nm": {
294+ "type": "string",
295+ "description": "Name by which a party is known"
296+ },
297+ "ctctDtls": {
298+ "type": "object",
299+ "properties": {
300+ "emailAdr": {
301+ "type": "string",
302+ "description": "Email address of the contact"
303+ },
304+ "phneNb": {
305+ "type": "string",
306+ "description": "Phone number of the contact"
307+ }
308+ }
309+ }
310+ },
311+ "required": ["nm"]
312+ },
313+ "CashAccount": {
314+ "type": "object",
315+ "properties": {
316+ "id": {
317+ "type": "object",
318+ "properties": {
319+ "iban": {
320+ "type": "string",
321+ "description": "International Bank Account Number (IBAN)"
322+ },
323+ "othr": {
324+ "type": "object",
325+ "properties": {
326+ "id": {
327+ "type": "string",
328+ "description": "Other account identification"
329+ }
330+ },
331+ "required": ["id"]
332+ }
333+ }
334+ },
335+ "tp": {
336+ "type": "object",
337+ "properties": {
338+ "cd": {
339+ "type": "string",
340+ "description": "Code specifying the nature or use of the account"
341+ }
342+ }
343+ },
344+ "ccy": {
345+ "type": "string",
346+ "description": "Identification of the currency in which the account is held"
347+ }
348+ },
349+ "required": ["id"]
350+ }
351+ }
352+ }""" .trimIndent()
353+
354+ // Execute the code generation - if it doesn't throw an exception, our fix works
355+ val result = json.generateKotlinClassCode(" PaymentInstruction" )
356+ println (result)
357+
358+ // Verify some basic expectations about the output
359+ assert (result.contains(" data class PaymentInstruction" )) { " Missing PaymentInstruction class" }
360+ assert (result.contains(" data class PaymentInformation" )) { " Missing PaymentInformation class" }
361+ assert (result.contains(" data class CreditTransferTransaction" )) { " Missing CreditTransferTransaction class" }
362+ assert (result.contains(" data class ActiveCurrencyAndAmount" )) { " Missing ActiveCurrencyAndAmount class" }
363+ assert (result.contains(" data class PartyIdentification" )) { " Missing PartyIdentification class" }
364+ assert (result.contains(" data class CashAccount" )) { " Missing CashAccount class" }
365+ }
64366}
0 commit comments