JSON Serialization and Deserialization

This page documents the JSON serialization and deserialization system within the Mesh Connect Android SDK. The system handles conversion between JSON messages received from web content and strongly-typed Kotlin data classes used throughout the native Android code.

For information about the specific data models and payload structures, see Data Models. For details about the message broadcasting system that uses these converters, see Message Broadcasting System.

Overview

The SDK uses a centralized JSON conversion system built around the JsonConverter class to handle bidirectional conversion between JSON strings and Kotlin objects. This system is primarily used for:

  • Deserializing messages received from embedded web content via the JSBridge
  • Converting typed Kotlin objects back to JSON for web communication
  • Type-safe handling of different message types based on JSON structure

Core Components

JsonConverter Class

The JsonConverter serves as the central conversion utility, providing methods for both serialization and deserialization operations. Based on the usage patterns in the codebase, it provides the following key methods:

  • fromJson(json: String, classOfT: Class<T>): T - Deserializes JSON to typed objects
  • toMap(json: String): Map<String, Any> - Converts JSON to key-value pairs for flexible access

Type-Based Message Processing

The system uses a type-based approach to determine how to deserialize incoming JSON messages. Each message contains a type field that maps to the Type enum:

Type Purpose Target Class
brokerageAccountAccessToken Account access tokens AccessTokenPayload
transferFinished Transfer completion TransferFinishedPayload
delayedAuthentication Delayed auth flow DelayedAuthPayload
error Error messages JsError
close Close events Simple event
done Done events Simple event
showClose Show close button Simple event
loaded Content loaded Simple event
openTrueAuth Open authentication Link extraction

Deserialization Flow

Message Type Detection and Routing

Raw JSON Message

JsType Deserialization

Extract type field

Type-based routing

Type.brokerageAccountAccessToken

Type.transferFinished

Type.delayedAuthentication

Type.error

Simple event types

Type.openTrueAuth

AccessTokenResponse

TransferFinishedResponse

DelayedAuthResponse

JsError

Link extraction via toMap()

AccessTokenPayload

TransferFinishedPayload

DelayedAuthPayload

LinkEvent.Payload

throw IllegalStateException

LinkEvent.TrueAuth

LinkEvent.Close/Done/etc

Payload Deserialization Process

Kotlin Objects

Deserialization Steps

JSON Structure

JSON Message

type: 'brokerageAccountAccessToken'

payload: { ... }

Extract JsType

Deserialize to ResponseWrapper

Extract payload field

AccessTokenPayload

List

BrandInfo

Token expiration data

Account objects

access/refresh tokens

Concrete Example: AccessTokenPayload

JSON Structure

The following JSON structure represents an access token response:

{
    "type": "brokerageAccountAccessToken",
    "payload": {
        "accountTokens": [{
            "account": {
                "accountId": "2wwwc105-5xx-4390x",
                "accountName": "xx9xxx226",
                "fund": 5.36,
                "cash": 5.26,
                "isReconnected": false,
                "frontAccountId": "wow_id"
            },
            "accessToken": "8/luwjLWf/QxxfilS2r",
            "refreshToken": "#34381lmefl93"
        }],
        "brokerType": "alpaca",
        "brokerName": "Alpaca",
        "brokerBrandInfo": {
            "brokerLogo": "iVBORwxxxxxSUhEUgAAA",
            "brokerPrimaryColor": "#FFF",
            "logoLightUrl": "http://logoLightUrl",
            "logoDarkUrl": "http://logoDarkUrl",
            "iconLightUrl": "http://iconLightUrl",
            "iconDarkUrl": "http://iconDarkUrl"
        },
        "expiresInSeconds": 200,
        "refreshTokenExpiresInSeconds": 300
    }
}

Resulting Kotlin Objects

The JSON deserializes to a hierarchy of Kotlin data classes:

AccessTokenPayload(
    accountTokens = listOf(
        AccountToken(
            account = Account(
                accountId = "2wwwc105-5xx-4390x",
                accountName = "xx9xxx226",
                fund = 5.36,
                cash = 5.26,
                isReconnected = false,
                frontAccountId = "wow_id"
            ),
            accessToken = "8/luwjLWf/QxxfilS2r",
            refreshToken = "#34381lmefl93"
        )
    ),
    brokerType = "alpaca",
    brokerName = "Alpaca",
    brokerBrandInfo = BrandInfo(
        brokerLogo = "iVBORwxxxxxSUhEUgAAA",
        brokerPrimaryColor = "#FFF",
        logoLightUrl = "http://logoLightUrl",
        logoDarkUrl = "http://logoDarkUrl",
        iconLightUrl = "http://iconLightUrl",
        iconDarkUrl = "http://iconDarkUrl"
    ),
    expiresInSeconds = 200,
    refreshTokenExpiresInSeconds = 300
)

Response Wrapper Pattern

The system uses a wrapper pattern for complex payloads where the actual data is nested within a payload field:

Message Structure Pattern

Actual Payload Classes

Response Wrapper Classes

JSON Message Format

Root JSON Object

type: string

payload: object

AccessTokenResponse

TransferFinishedResponse

DelayedAuthResponse

AccessTokenPayload

TransferFinishedPayload

DelayedAuthPayload

Error Handling

The deserialization system includes error handling for malformed or error-type messages:

  1. Type Detection Errors - Unknown or null types return null from the deserialization process
  2. Error Messages - Messages with type: "error" are deserialized to JsError objects and throw IllegalStateException
  3. Deserialization Failures - JSON parsing errors are handled by the underlying JSON converter implementation

Error Message Processing

JSON with type: 'error'

Deserialize to JsError

Extract errorMessage field

Use default if null

throw IllegalStateException

Special Cases

TrueAuth Link Extraction

For openTrueAuth messages, the system uses a different approach, converting the JSON to a Map and extracting the link field directly:

Type.openTrueAuth -> {
    val link = jsonConverter.toMap(json)["link"]
    LinkEvent.TrueAuth(link.toString())
}

Simple Event Types

Some message types like close, done, showClose, and loaded don't require deserialization of complex payloads and map directly to sealed LinkEvent objects.

Testing Framework

The system includes comprehensive testing for JSON deserialization:

  • Unit Tests - Test individual payload deserialization with mock JSON data
  • Integration Tests - Test the complete flow from JSON to LinkEvent objects
  • Sample Data - Test resources contain realistic JSON examples for validation

The test framework uses a combination of actual JSON files and programmatically constructed expected objects to validate the deserialization accuracy.