All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amplitude.core.utilities.JSONUtil.kt Maven / Gradle / Ivy

The newest version!
package com.amplitude.core.utilities

import com.amplitude.core.Constants
import com.amplitude.core.events.BaseEvent
import com.amplitude.core.events.IngestionMetadata
import com.amplitude.core.events.Plan
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

object JSONUtil {
    fun eventToJsonObject(event: BaseEvent): JSONObject {
        val eventJSON = JSONObject()
        eventJSON.put("event_type", event.eventType)
        eventJSON.addValue("user_id", event.userId)
        eventJSON.addValue("device_id", event.deviceId)
        eventJSON.addValue("time", event.timestamp)
        eventJSON.addValue("event_properties", truncate(event.eventProperties.toJSONObject()))
        eventJSON.addValue("user_properties", truncate(event.userProperties.toJSONObject()))
        eventJSON.addValue("groups", truncate(event.groups.toJSONObject()))
        eventJSON.addValue("group_properties", truncate(event.groupProperties.toJSONObject()))
        eventJSON.addValue("app_version", event.appVersion)
        eventJSON.addValue("platform", event.platform)
        eventJSON.addValue("os_name", event.osName)
        eventJSON.addValue("os_version", event.osVersion)
        eventJSON.addValue("device_brand", event.deviceBrand)
        eventJSON.addValue("device_manufacturer", event.deviceManufacturer)
        eventJSON.addValue("device_model", event.deviceModel)
        eventJSON.addValue("carrier", event.carrier)
        eventJSON.addValue("country", event.country)
        eventJSON.addValue("region", event.region)
        eventJSON.addValue("city", event.city)
        eventJSON.addValue("dma", event.dma)
        eventJSON.addValue("language", event.language)
        eventJSON.addValue("price", event.price)
        eventJSON.addValue("quantity", event.quantity)
        eventJSON.addValue("revenue", event.revenue)
        eventJSON.addValue("productId", event.productId)
        eventJSON.addValue("revenueType", event.revenueType)
        eventJSON.addValue("currency", event.currency)
        eventJSON.addValue("location_lat", event.locationLat)
        eventJSON.addValue("location_lng", event.locationLng)
        eventJSON.addValue("ip", event.ip)
        eventJSON.addValue("version_name", event.versionName)
        eventJSON.addValue("idfa", event.idfa)
        eventJSON.addValue("idfv", event.idfv)
        eventJSON.addValue("adid", event.adid)
        eventJSON.addValue("android_id", event.androidId)
        eventJSON.addValue("event_id", event.eventId)
        eventJSON.addValue("session_id", event.sessionId)
        eventJSON.addValue("insert_id", event.insertId)
        eventJSON.addValue("library", event.library)
        eventJSON.addValue("partner_id", event.partnerId)
        eventJSON.addValue("android_app_set_id", event.appSetId)
        event.plan?. let {
            eventJSON.put("plan", it.toJSONObject())
        }
        event.ingestionMetadata?. let {
            eventJSON.put("ingestion_metadata", it.toJSONObject())
        }
        return eventJSON
    }

    fun eventToString(event: BaseEvent): String {
        return eventToJsonObject(event).toString()
    }

    fun eventsToString(events: List): String {
        if (events.isEmpty()) {
            return ""
        }
        val eventsArray = JSONArray()
        for (event in events) {
            eventsArray.put(eventToJsonObject(event))
        }
        return eventsArray.toString()
    }

    private fun truncate(obj: JSONObject?): JSONObject {
        if (obj == null) {
            return JSONObject()
        }
        if (obj.length() > Constants.MAX_PROPERTY_KEYS) {
            throw IllegalArgumentException("Too many properties (more than " + Constants.MAX_PROPERTY_KEYS.toString() + ") in JSON")
        }
        val keys: Iterator<*> = obj.keys()
        while (keys.hasNext()) {
            val key = keys.next() as String
            try {
                val value = obj[key]
                if (value.javaClass == String::class.java) {
                    obj.put(key, truncate(value as String))
                } else if (value.javaClass == JSONObject::class.java) {
                    obj.put(key, truncate(value as JSONObject))
                } else if (value.javaClass == JSONArray::class.java) {
                    obj.put(key, truncate(value as JSONArray))
                }
            } catch (e: JSONException) {
                throw IllegalArgumentException(
                    "JSON parsing error. Too long (> ${Constants.MAX_STRING_LENGTH} chars) or invalid JSON",
                )
            }
        }
        return obj
    }

    @Throws(JSONException::class)
    fun truncate(array: JSONArray?): JSONArray {
        if (array == null) {
            return JSONArray()
        }
        for (i in 0 until array.length()) {
            val value = array[i]
            if ((value.javaClass == String::class.java)) {
                array.put(i, truncate(value as String))
            } else if ((value.javaClass == JSONObject::class.java)) {
                array.put(i, truncate(value as JSONObject))
            } else if ((value.javaClass == JSONArray::class.java)) {
                array.put(i, truncate(value as JSONArray))
            }
        }
        return array
    }

    private fun truncate(value: String): String {
        return if (value.length <= Constants.MAX_STRING_LENGTH) {
            value
        } else {
            value.substring(
                0,
                Constants.MAX_STRING_LENGTH,
            )
        }
    }
}

internal fun JSONObject.getStringWithDefault(
    key: String,
    defaultValue: String,
): String {
    if (this.has(key)) {
        return this.getString(key)
    }
    return defaultValue
}

internal fun JSONObject.collectIndices(): Set {
    val indices = mutableListOf()
    val fieldKeys: Iterator = this.keys()
    while (fieldKeys.hasNext()) {
        val fieldKey = fieldKeys.next()
        val eventIndices: IntArray = this.getJSONArray(fieldKey).toIntArray()
        for (eventIndex in eventIndices) {
            indices.add(eventIndex)
        }
    }
    return indices.toSet()
}

internal fun JSONArray.toIntArray(): IntArray {
    val intArray = IntArray(this.length())
    for (i in intArray.indices) {
        intArray[i] = this.optInt(i)
    }
    return intArray
}

fun JSONObject.toBaseEvent(): BaseEvent {
    val event = BaseEvent()
    event.eventType = this.getString("event_type")
    event.userId = this.optionalString("user_id", null)
    event.deviceId = this.optionalString("device_id", null)
    event.timestamp = if (this.has("time")) this.getLong("time") else null
    event.eventProperties = this.optionalJSONObject("event_properties", null)?.toMapObj()?.toMutableMap()
    event.userProperties = this.optionalJSONObject("user_properties", null)?.toMapObj()?.toMutableMap()
    event.groups = this.optionalJSONObject("groups", null)?.toMapObj()?.toMutableMap()
    event.groupProperties = this.optionalJSONObject("group_properties", null)?.toMapObj()?.toMutableMap()
    event.appVersion = this.optionalString("app_version", null)
    event.platform = this.optionalString("platform", null)
    event.osName = this.optionalString("os_name", null)
    event.osVersion = this.optionalString("os_version", null)
    event.deviceBrand = this.optionalString("device_brand", null)
    event.deviceManufacturer = this.optionalString("device_manufacturer", null)
    event.deviceModel = this.optionalString("device_model", null)
    event.carrier = this.optionalString("carrier", null)
    event.country = this.optionalString("country", null)
    event.region = this.optionalString("region", null)
    event.city = this.optionalString("city", null)
    event.dma = this.optionalString("dma", null)
    event.language = this.optionalString("language", null)
    event.price = if (this.has("price")) this.getDouble("price") else null
    event.quantity = if (this.has("quantity")) this.getInt("quantity") else null
    event.revenue = if (this.has("revenue")) this.getDouble("revenue") else null
    event.productId = this.optionalString("productId", null)
    event.revenueType = this.optionalString("revenueType", null)
    event.locationLat = if (this.has("location_lat")) this.getDouble("location_lat") else null
    event.locationLng = if (this.has("location_lng")) this.getDouble("location_lng") else null
    event.ip = this.optionalString("ip", null)
    event.idfa = this.optionalString("idfa", null)
    event.idfv = this.optionalString("idfv", null)
    event.adid = this.optionalString("adid", null)
    event.androidId = this.optionalString("android_id", null)
    event.appSetId = this.optString("android_app_set_id", null)
    event.eventId = if (this.has("event_id")) this.getLong("event_id") else null
    event.sessionId = if (this.has("session_id")) this.getLong("session_id") else null
    event.insertId = this.optionalString("insert_id", null)
    event.library = if (this.has("library")) this.getString("library") else null
    event.partnerId = this.optionalString("partner_id", null)
    event.plan = if (this.has("plan")) Plan.fromJSONObject(this.getJSONObject("plan")) else null
    event.ingestionMetadata =
        if (this.has(
                "ingestion_metadata",
            )
        ) {
            IngestionMetadata.fromJSONObject(this.getJSONObject("ingestion_metadata"))
        } else {
            null
        }
    return event
}

fun JSONArray.toEvents(): List {
    val events = mutableListOf()
    (0 until this.length()).forEach {
        events.add((this.getJSONObject(it)).toBaseEvent())
    }
    return events
}

internal fun JSONArray.split(): Pair, List> {
    val mid = this.length() / 2
    val firstHalf = mutableListOf()
    val secondHalf = mutableListOf()
    (0 until this.length()).forEach { index ->
        if (index < mid) {
            firstHalf.add(this.getJSONObject(index))
        } else {
            secondHalf.add(this.getJSONObject(index))
        }
    }
    return Pair(firstHalf, secondHalf)
}

internal fun JSONArray.toJSONObjectList(): List {
    val list = mutableListOf()
    (0 until this.length()).forEach {
        list.add(this.getJSONObject(it))
    }
    return list
}

internal fun JSONObject.addValue(
    key: String,
    value: Any?,
) {
    value?.let {
        this.put(key, value)
    }
}

fun JSONObject.optionalJSONObject(
    key: String,
    defaultValue: JSONObject?,
): JSONObject? {
    if (this.has(key)) {
        return this.getJSONObject(key)
    }
    return defaultValue
}

fun JSONObject.optionalString(
    key: String,
    defaultValue: String?,
): String? {
    if (this.has(key)) {
        return this.getString(key)
    }
    return defaultValue
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy