@@ -13,6 +13,7 @@ import org.hyperledger.iroha.sdk.address.decodeCompactPublicKeyPayload
1313import org.hyperledger.iroha.sdk.address.requireCanonicalI105Address
1414import org.hyperledger.iroha.sdk.core.model.Executable
1515import org.hyperledger.iroha.sdk.core.model.InstructionBox
16+ import org.hyperledger.iroha.sdk.core.model.JsonValue
1617import org.hyperledger.iroha.sdk.core.model.TransactionPayload
1718import org.hyperledger.iroha.sdk.core.model.WirePayload
1819import org.hyperledger.iroha.sdk.norito.NoritoAdapters
@@ -302,23 +303,23 @@ internal class TransactionPayloadAdapter : TypeAdapter<TransactionPayload> {
302303 }
303304 }
304305
305- private class JsonAdapter : TypeAdapter <String > {
306+ private class JsonValueFieldAdapter : TypeAdapter <String > {
306307 override fun encode (encoder : NoritoEncoder , value : String ) {
307- encodeSizedField(encoder, JSON_STRING_ADAPTER , value)
308+ encodeSizedField(encoder, STRING_ADAPTER , value)
308309 }
309310
310311 override fun decode (decoder : NoritoDecoder ): String {
311- return decodeSizedField(decoder, JSON_STRING_ADAPTER )
312+ return decodeSizedField(decoder, STRING_ADAPTER )
312313 }
313314
314315 override fun isSelfDelimiting (): Boolean = true
315316 }
316317
317- private class MetadataAdapter : TypeAdapter <Map <String , String >> {
318+ private class MetadataAdapter : TypeAdapter <Map <String , JsonValue >> {
318319 private val entryListAdapter: TypeAdapter <List <MetadataEntry >> =
319320 NoritoAdapters .sequence(MetadataEntryAdapter ())
320321
321- override fun encode (encoder : NoritoEncoder , value : Map <String , String >) {
322+ override fun encode (encoder : NoritoEncoder , value : Map <String , JsonValue >) {
322323 val keys = value.keys.sorted()
323324 val entries = keys.map { key ->
324325 val entryValue = value[key]
@@ -328,50 +329,36 @@ internal class TransactionPayloadAdapter : TypeAdapter<TransactionPayload> {
328329 entryListAdapter.encode(encoder, entries)
329330 }
330331
331- override fun decode (decoder : NoritoDecoder ): Map <String , String > {
332+ override fun decode (decoder : NoritoDecoder ): Map <String , JsonValue > {
332333 val entries = entryListAdapter.decode(decoder)
333- val decoded = LinkedHashMap <String , String >(entries.size)
334+ val decoded = LinkedHashMap <String , JsonValue >(entries.size)
334335 for (entry in entries) {
335336 require(decoded.put(entry.key, entry.value) == null ) { " Duplicate metadata key" }
336337 }
337338 return decoded
338339 }
339340 }
340341
341- private class MetadataEntry (val key : String , val value : String )
342+ private class MetadataEntry (val key : String , val value : JsonValue )
342343
343344 private class MetadataEntryAdapter : TypeAdapter <MetadataEntry > {
344345 override fun encode (encoder : NoritoEncoder , value : MetadataEntry ) {
345346 encodeSizedField(encoder, STRING_ADAPTER , value.key)
346- encodeSizedField(encoder, JSON_ADAPTER , value.value)
347+ encodeSizedField(encoder, JSON_VALUE_ADAPTER , value.value.rawJson )
347348 }
348349
349350 override fun decode (decoder : NoritoDecoder ): MetadataEntry {
350351 val key = decodeSizedField(decoder, STRING_ADAPTER )
351- val value = decodeSizedField(decoder, JSON_ADAPTER )
352- return MetadataEntry (key, value)
353- }
354- }
355-
356- private class JsonStringAdapter : TypeAdapter <String > {
357- override fun encode (encoder : NoritoEncoder , value : String ) {
358- STRING_ADAPTER .encode(encoder, encodeJsonString(value))
359- }
360-
361- override fun decode (decoder : NoritoDecoder ): String {
362- val raw = STRING_ADAPTER .decode(decoder)
363- return decodeJsonString(raw)
352+ val raw = decodeSizedField(decoder, JSON_VALUE_ADAPTER )
353+ return MetadataEntry (key, JsonValue .raw(raw))
364354 }
365-
366- override fun isSelfDelimiting (): Boolean = true
367355 }
368356
369357 companion object {
370358 private val STRING_ADAPTER : TypeAdapter <String > = NoritoAdapters .stringAdapter()
371359 private val ACCOUNT_ID_ADAPTER : TypeAdapter <String > = AccountIdAdapter ()
372360 private val CHAIN_ID_ADAPTER : TypeAdapter <String > = ChainIdAdapter ()
373- private val JSON_STRING_ADAPTER : TypeAdapter <String > = JsonStringAdapter ()
374- private val JSON_ADAPTER : TypeAdapter <String > = JsonAdapter ()
361+ private val JSON_VALUE_ADAPTER : TypeAdapter <String > = JsonValueFieldAdapter ()
375362 private val UINT64_ADAPTER : TypeAdapter <Long > = NoritoAdapters .uint(64 )
376363 private val UINT16_ADAPTER : TypeAdapter <Long > = NoritoAdapters .uint(16 )
377364 private val UINT8_ADAPTER : TypeAdapter <Long > = NoritoAdapters .uint(8 )
@@ -388,11 +375,9 @@ internal class TransactionPayloadAdapter : TypeAdapter<TransactionPayload> {
388375 private val TTL_ADAPTER : TypeAdapter <Optional <Long >> = NoritoAdapters .option(NoritoAdapters .uint(64 ))
389376 private val NONCE_ADAPTER : TypeAdapter <Optional <Long >> = NoritoAdapters .option(NoritoAdapters .uint(32 ))
390377 private val EXECUTABLE_ADAPTER : TypeAdapter <Executable > = ExecutableAdapter ()
391- private val METADATA_ADAPTER : TypeAdapter <Map <String , String >> = MetadataAdapter ()
378+ private val METADATA_ADAPTER : TypeAdapter <Map <String , JsonValue >> = MetadataAdapter ()
392379 private const val INSTRUCTION_BOX_SCHEMA = " iroha.data_model.isi.InstructionBox.v1"
393380
394- private val HEX_DIGITS = " 0123456789ABCDEF" .toCharArray()
395-
396381 internal fun encodeInstructionBox (value : InstructionBox ): ByteArray =
397382 NoritoCodec .encode(value, INSTRUCTION_BOX_SCHEMA , InstructionAdapter ())
398383
@@ -479,85 +464,5 @@ internal class TransactionPayloadAdapter : TypeAdapter<TransactionPayload> {
479464 false
480465 }
481466 }
482-
483- private fun encodeJsonString (value : String ): String {
484- val builder = StringBuilder (value.length + 2 )
485- builder.append(' "' )
486- for (c in value) {
487- when (c) {
488- ' "' -> builder.append(" \\\" " )
489- ' \\ ' -> builder.append(" \\\\ " )
490- ' \b ' -> builder.append(" \\ b" )
491- ' \u000C ' -> builder.append(" \\ f" )
492- ' \n ' -> builder.append(" \\ n" )
493- ' \r ' -> builder.append(" \\ r" )
494- ' \t ' -> builder.append(" \\ t" )
495- else -> {
496- if (c < ' \u0020 ' ) {
497- builder.append(" \\ u00" )
498- builder.append(HEX_DIGITS [(c.code shr 4 ) and 0xF ])
499- builder.append(HEX_DIGITS [c.code and 0xF ])
500- } else {
501- builder.append(c)
502- }
503- }
504- }
505- }
506- builder.append(' "' )
507- return builder.toString()
508- }
509-
510- private fun decodeJsonString (raw : String? ): String {
511- if (raw == null ) return raw.toString()
512- val trimmed = raw.trim()
513- if (trimmed.length < 2 || trimmed[0 ] != ' "' || trimmed[trimmed.length - 1 ] != ' "' ) return raw
514- return try {
515- parseJsonString(trimmed)
516- } catch (_: IllegalArgumentException ) {
517- raw
518- }
519- }
520-
521- private fun parseJsonString (input : String ): String {
522- val builder = StringBuilder ()
523- var i = 1
524- while (i < input.length - 1 ) {
525- val c = input[i++ ]
526- if (c == ' \\ ' ) {
527- require(i < input.length - 1 ) { " Invalid JSON escape" }
528- val esc = input[i++ ]
529- when (esc) {
530- ' "' -> builder.append(' "' )
531- ' \\ ' -> builder.append(' \\ ' )
532- ' /' -> builder.append(' /' )
533- ' b' -> builder.append(' \b ' )
534- ' f' -> builder.append(' \u000C ' )
535- ' n' -> builder.append(' \n ' )
536- ' r' -> builder.append(' \r ' )
537- ' t' -> builder.append(' \t ' )
538- ' u' -> {
539- require(i + 4 <= input.length - 1 ) { " Invalid unicode escape" }
540- var codePoint = 0
541- for (j in 0 until 4 ) {
542- codePoint = (codePoint shl 4 ) or hexNibble(input[i + j])
543- }
544- builder.append(codePoint.toChar())
545- i + = 4
546- }
547- else -> throw IllegalArgumentException (" Unsupported escape: \\ $esc " )
548- }
549- } else {
550- builder.append(c)
551- }
552- }
553- return builder.toString()
554- }
555-
556- private fun hexNibble (c : Char ): Int = when (c) {
557- in ' 0' .. ' 9' -> c - ' 0'
558- in ' a' .. ' f' -> 10 + (c - ' a' )
559- in ' A' .. ' F' -> 10 + (c - ' A' )
560- else -> throw IllegalArgumentException (" Invalid hex digit: $c " )
561- }
562467 }
563468}
0 commit comments