Exemples de Sérialisation Android Kotlin

Exemples de sérialisation Android Kotlin incluant la sérialisation JSON, la désérialisation et l'analyse XML

💻 Sérialisation JSON kotlin

🟡 intermediate ⭐⭐⭐

Convertir des objets en chaînes JSON en utilisant Gson et d'autres bibliothèques

⏱️ 25 min 🏷️ kotlin, android, json, serialization
Prerequisites: Intermediate Kotlin, Gson library
// Android Kotlin JSON Serialization Examples
// Using Gson library and kotlinx.serialization

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.util.Date

// Data classes for serialization
data class User(
    val id: Int,
    val username: String,
    val email: String,
    val age: Int? = null,
    val isActive: Boolean = true,
    val createdAt: Date? = null
)

data class Product(
    val productId: String,
    val name: String,
    val price: Double,
    val tags: List<String>,
    val inStock: Boolean
)

data class Order(
    val orderId: Int,
    val user: User,
    val products: List<Product>,
    val totalAmount: Double,
    val orderDate: String
)

// 1. Basic JSON Serialization with Gson
class BasicJsonSerializer {

    private val gson: Gson = Gson()

    // Serialize simple object
    fun serializeObject(obj: Any): String {
        val json = gson.toJson(obj)
        println("Serialized: $json")
        return json
    }

    // Serialize with pretty printing
    fun serializeObjectPretty(obj: Any): String {
        val prettyGson = GsonBuilder()
            .setPrettyPrinting()
            .create()

        val json = prettyGson.toJson(obj)
        println("Serialized (pretty):\n$json")
        return json
    }

    // Serialize null values explicitly
    fun serializeWithNulls(obj: Any): String {
        val gsonWithNulls = GsonBuilder()
            .serializeNulls()
            .create()

        return gsonWithNulls.toJson(obj)
    }

    // Serialize date in custom format
    fun serializeWithDateFormat(obj: Any, dateFormat: String): String {
        val gsonWithDateFormat = GsonBuilder()
            .setDateFormat(dateFormat)
            .create()

        return gsonWithDateFormat.toJson(obj)
    }

    // Serialize with custom field naming
    fun serializeWithFieldNaming(obj: Any): String {
        val gsonWithLowercase = GsonBuilder()
            .setFieldNamingPolicy(
                com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
            )
            .create()

        return gsonWithLowercase.toJson(obj)
    }
}

// 2. Advanced JSON Serialization
class AdvancedJsonSerializer {

    // Serialize with exclusion strategy
    fun serializeWithExclusion(obj: Any): String {
        val gson = GsonBuilder()
            .setExclusionStrategies(object : com.google.gson.ExclusionStrategy {
                override fun shouldSkipField(field: com.google.gson.FieldAttributes): Boolean {
                    // Skip fields with specific annotation
                    return field.getAnnotation(SkipSerialization::class.java) != null
                }

                override fun shouldSkipClass(clazz: Class<*>): Boolean {
                    return false
                }
            })
            .create()

        return gson.toJson(obj)
    }

    // Serialize with custom serializer
    fun serializeWithCustomSerializer(obj: Any): String {
        val gson = GsonBuilder()
            .registerTypeAdapter(Date::class.java, DateSerializer())
            .create()

        return gson.toJson(obj)
    }

    // Serialize to file
    fun serializeToFile(obj: Any, filePath: String): Boolean {
        return try {
            val gson = Gson()
            val json = gson.toJson(obj)

            java.io.FileWriter(filePath).use { writer ->
                writer.write(json)
            }

            println("Serialized to file: $filePath")
            true
        } catch (e: Exception) {
            println("Error serializing to file: ${e.message}")
            false
        }
    }

    // Serialize with versioning
    fun serializeWithVersion(obj: Any, version: Double): String {
        val gson = GsonBuilder()
            .setVersion(version)
            .create()

        return gson.toJson(obj)
    }
}

// 3. Collection Serialization
class CollectionSerializer {

    private val gson: Gson = Gson()

    // Serialize list
    fun serializeList(list: List<Any>): String {
        return gson.toJson(list)
    }

    // Serialize map
    fun serializeMap(map: Map<String, Any>): String {
        return gson.toJson(map)
    }

    // Serialize set
    fun serializeSet(set: Set<Any>): String {
        return gson.toJson(set)
    }

    // Serialize nested collections
    fun serializeNested(data: Map<String, List<Any>>): String {
        return gson.toJson(data)
    }

    // Serialize array
    fun serializeArray(array: Array<Any>): String {
        return gson.toJson(array)
    }
}

// 4. Streaming JSON Serialization
class StreamingJsonSerializer {

    // Serialize using JsonWriter (for large datasets)
    fun serializeUsingJsonWriter(data: List<User>, filePath: String): Boolean {
        return try {
            java.io.FileWriter(filePath).use { writer ->
                val jsonWriter = com.google.gson.stream.JsonWriter(writer)

                jsonWriter.beginArray()
                for (user in data) {
                    jsonWriter.beginObject()

                    jsonWriter.name("id").value(user.id.toLong())
                    jsonWriter.name("username").value(user.username)
                    jsonWriter.name("email").value(user.email)
                    user.age?.let { jsonWriter.name("age").value(it.toLong()) }
                    jsonWriter.name("isActive").value(user.isActive)

                    jsonWriter.endObject()
                }
                jsonWriter.endArray()

                jsonWriter.close()
            }

            println("Streamed ${data.size} items to $filePath")
            true
        } catch (e: Exception) {
            println("Error streaming JSON: ${e.message}")
            false
        }
    }

    // Serialize with buffering
    fun serializeWithBuffer(data: List<Any>, bufferSize: Int): List<String> {
        val chunks = mutableListOf<String>()
        val gson = Gson()

        data.chunked(bufferSize).forEach { chunk ->
            chunks.add(gson.toJson(chunk))
        }

        return chunks
    }
}

// 5. Kotlinx Serialization (Alternative)
// Note: Requires kotlinx-serialization plugin
object KotlinxSerializer {

    // Example using kotlinx.serialization (if plugin is enabled)
    // @Serializable
    // data class UserKotlinx(
    //     val id: Int,
    //     val username: String,
    //     val email: String
    // )

    // fun serializeKotlinx(obj: Any): String {
    //     return Json.encodeToString(obj)
    // }
}

// 6. Custom Date Serializer
class DateSerializer : com.google.gson.JsonSerializer<Date> {
    override fun serialize(
                    src: Date,
                    typeOfSrc: Type,
                    context: com.google.gson.JsonSerializationContext
    ): com.google.gson.JsonElement {
        val formatter = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
        return com.google.gson.JsonPrimitive(formatter.format(src))
    }
}

// Annotation for skipping fields
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class SkipSerialization

// Main demonstration
fun demonstrateJsonSerialization() {
    println("=== Android Kotlin JSON Serialization Examples ===\n")

    // 1. Basic serialization
    println("--- 1. Basic Serialization ---")
    val basicSerializer = BasicJsonSerializer()

    val user = User(
        id = 1,
        username = "alice",
        email = "[email protected]",
        age = 25,
        isActive = true,
        createdAt = Date()
    )

    val json1 = basicSerializer.serializeObject(user)
    println("Simple: $json1")

    // 2. Pretty printing
    println("\n--- 2. Pretty Printing ---")
    val json2 = basicSerializer.serializeObjectPretty(user)

    // 3. Serialization with nulls
    println("\n--- 3. Serialize with Nulls ---")
    val userWithNulls = User(
        id = 2,
        username = "bob",
        email = "[email protected]",
        age = null,
        isActive = true
    )

    val json3 = basicSerializer.serializeWithNulls(userWithNulls)
    println("With nulls: $json3")

    // 4. Date formatting
    println("\n--- 4. Date Formatting ---")
    val json4 = basicSerializer.serializeWithDateFormat(
        user,
        "yyyy-MM-dd HH:mm:ss"
    )
    println("Custom date format: $json4")

    // 5. Complex object
    println("\n--- 5. Complex Object Serialization ---")
    val order = Order(
        orderId = 1001,
        user = user,
        products = listOf(
            Product("P001", "Laptop", 999.99, listOf("Electronics", "Computers"), true),
            Product("P002", "Mouse", 29.99, listOf("Electronics", "Accessories"), true)
        ),
        totalAmount = 1029.98,
        orderDate = "2025-12-27"
    )

    val json5 = basicSerializer.serializeObjectPretty(order)
    println("Order:\n$json5")

    // 6. Collections
    println("\n--- 6. Collection Serialization ---")
    val collectionSerializer = CollectionSerializer()

    val users = listOf(
        User(1, "alice", "[email protected]", 25),
        User(2, "bob", "[email protected]", 30),
        User(3, "charlie", "[email protected]", 28)
    )

    val usersJson = collectionSerializer.serializeList(users)
    println("Users list: $usersJson")

    val userMap = mapOf(
        "user1" to users[0],
        "user2" to users[1],
        "user3" to users[2]
    )

    val mapJson = collectionSerializer.serializeMap(userMap)
    println("Users map: $mapJson")

    // 7. Field naming policy
    println("\n--- 7. Field Naming Policy ---")
    val json6 = basicSerializer.serializeWithFieldNaming(user)
    println("Lowercase with underscores: $json6")

    // 8. Versioning
    println("\n--- 8. Versioning ---")
    val advancedSerializer = AdvancedJsonSerializer()
    val json7 = advancedSerializer.serializeWithVersion(user, 1.0)
    println("Version 1.0: $json7")

    println("\n=== All JSON Serialization Examples Completed ===")
}

💻 Désérialisation JSON kotlin

🟡 intermediate ⭐⭐⭐⭐

Analyser les chaînes JSON et les convertir en objets en utilisant Gson

⏱️ 30 min 🏷️ kotlin, android, json, parsing
Prerequisites: Intermediate Kotlin, Gson library
// Android Kotlin JSON Deserialization Examples
// Using Gson library for parsing JSON

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.google.gson.JsonSyntaxException
import java.lang.reflect.Type

// Data classes for deserialization
data class User(
    val id: Int,
    val username: String,
    val email: String,
    val age: Int? = null,
    val isActive: Boolean = true,
    val createdAt: String? = null
)

data class Product(
    val productId: String,
    val name: String,
    val price: Double,
    val tags: List<String>,
    val inStock: Boolean
)

data class ApiResponse<T>(
    val success: Boolean,
    val data: T?,
    val message: String?,
    val errorCode: Int?
)

// 1. Basic JSON Deserialization
class BasicJsonDeserializer {

    private val gson: Gson = Gson()

    // Deserialize to simple object
    fun <T> deserializeObject(json: String, classOfT: Class<T>): T? {
        return try {
            val obj = gson.fromJson(json, classOfT)
            println("Deserialized: $obj")
            obj
        } catch (e: JsonSyntaxException) {
            println("Error deserializing: ${e.message}")
            null
        }
    }

    // Deserialize with pretty JSON handling
    fun <T> deserializePretty(json: String, classOfT: Class<T>): T? {
        return try {
            gson.fromJson(json, classOfT)
        } catch (e: Exception) {
            println("Error: ${e.message}")
            null
        }
    }

    // Deserialize with default values
    fun deserializeWithDefaults(json: String): User? {
        return try {
            gson.fromJson(json, User::class.java)
        } catch (e: Exception) {
            println("Error: ${e.message}")
            null
        }
    }
}

// 2. Collection Deserialization
class CollectionDeserializer {

    private val gson: Gson = Gson()

    // Deserialize JSON array to list
    fun <T> deserializeList(json: String, classOfT: Class<T>): List<T> {
        val listType = TypeToken.getParameterized(List::class.java, classOfT).type
        return try {
            gson.fromJson(json, listType) ?: emptyList()
        } catch (e: Exception) {
            println("Error deserializing list: ${e.message}")
            emptyList()
        }
    }

    // Deserialize JSON object to map
    fun deserializeMap(json: String): Map<String, Any> {
        val mapType = object : TypeToken<Map<String, Any>>() {}.type
        return try {
            gson.fromJson(json, mapType) ?: emptyMap()
        } catch (e: Exception) {
            println("Error deserializing map: ${e.message}")
            emptyMap()
        }
    }

    // Deserialize to set
    fun <T> deserializeSet(json: String, classOfT: Class<T>): Set<T> {
        val setType = TypeToken.getParameterized(Set::class.java, classOfT).type
        return try {
            gson.fromJson(json, setType) ?: emptySet()
        } catch (e: Exception) {
            println("Error deserializing set: ${e.message}")
            emptySet()
        }
    }

    // Deserialize nested collections
    fun deserializeNestedList(json: String): List<List<String>> {
        val nestedType = object : TypeToken<List<List<String>>>() {}.type
        return try {
            gson.fromJson(json, nestedType) ?: emptyList()
        } catch (e: Exception) {
            println("Error deserializing nested list: ${e.message}")
            emptyList()
        }
    }
}

// 3. Advanced Deserialization
class AdvancedJsonDeserializer {

    private val gson: Gson = Gson()

    // Deserialize from file
    fun <T> deserializeFromFile(filePath: String, classOfT: Class<T>): T? {
        return try {
            val file = java.io.File(filePath)
            val json = file.readText()

            gson.fromJson(json, classOfT)
        } catch (e: Exception) {
            println("Error reading from file: ${e.message}")
            null
        }
    }

    // Deserialize with custom date format
    fun deserializeWithDateFormat(json: String, dateFormat: String): User? {
        val gsonWithDateFormat = GsonBuilder()
            .setDateFormat(dateFormat)
            .create()

        return try {
            gsonWithDateFormat.fromJson(json, User::class.java)
        } catch (e: Exception) {
            println("Error with custom date format: ${e.message}")
            null
        }
    }

    // Deserialize with lenient mode
    fun deserializeLenient(json: String): User? {
        val lenientGson = GsonBuilder()
            .setLenient()
            .create()

        return try {
            lenientGson.fromJson(json, User::class.java)
        } catch (e: Exception) {
            println("Error in lenient mode: ${e.message}")
            null
        }
    }

    // Deserialize generic type
    fun <T> deserializeGeneric(json: String, type: Type): T? {
        return try {
            gson.fromJson(json, type)
        } catch (e: Exception) {
            println("Error deserializing generic: ${e.message}")
            null
        }
    }

    // Deserialize ApiResponse wrapper
    fun <T> deserializeApiResponse(json: String, dataType: Type): ApiResponse<T>? {
        val apiResponseType = TypeToken.getParameterized(
            ApiResponse::class.java,
            dataType
        ).type

        return try {
            gson.fromJson(json, apiResponseType)
        } catch (e: Exception) {
            println("Error deserializing API response: ${e.message}")
            null
        }
    }
}

// 4. Safe Deserialization with Validation
class SafeJsonDeserializer {

    private val gson: Gson = Gson()

    // Deserialize with validation
    fun <T> deserializeWithValidation(
                    json: String,
                    classOfT: Class<T>,
                    validator: (T) -> Boolean
    ): T? {
        return try {
            val obj = gson.fromJson(json, classOfT)

            if (validator(obj)) {
                obj
            } else {
                println("Validation failed")
                null
            }
        } catch (e: Exception) {
            println("Error: ${e.message}")
            null
        }
    }

    // Deserialize with fallback
    fun <T> deserializeWithFallback(
                    json: String,
                    classOfT: Class<T>,
                    fallback: T
    ): T {
        return try {
            gson.fromJson(json, classOfT) ?: fallback
        } catch (e: Exception) {
            println("Error, using fallback: ${e.message}")
            fallback
        }
    }

    // Deserialize nullable fields
    fun deserializeNullable(json: String): User? {
        return try {
            gson.fromJson(json, User::class.java)
        } catch (e: Exception) {
            println("Error with nullable: ${e.message}")
            null
        }
    }
}

// 5. Streaming JSON Deserialization
class StreamingJsonDeserializer {

    // Parse large JSON files using JsonReader
    fun parseLargeJson(filePath: String): List<User> {
        val users = mutableListOf<User>()

        try {
            java.io.FileReader(filePath).use { reader ->
                val jsonReader = com.google.gson.stream.JsonReader(reader)

                jsonReader.beginArray()

                while (jsonReader.hasNext()) {
                    jsonReader.beginObject()

                    var id = 0
                    var username = ""
                    var email = ""
                    var age: Int? = null
                    var isActive = true

                    while (jsonReader.hasNext()) {
                        when (jsonReader.nextName()) {
                            "id" -> id = jsonReader.nextInt()
                            "username" -> username = jsonReader.nextString()
                            "email" -> email = jsonReader.nextString()
                            "age" -> age = jsonReader.nextInt()
                            "isActive" -> isActive = jsonReader.nextBoolean()
                        }
                    }

                    jsonReader.endObject()

                    users.add(User(id, username, email, age, isActive))
                }

                jsonReader.endArray()
                jsonReader.close()
            }
        } catch (e: Exception) {
            println("Error streaming JSON: ${e.message}")
        }

        return users
    }

    // Parse specific fields only
    fun parseSpecificFields(json: String, fields: Set<String>): Map<String, String> {
        val result = mutableMapOf<String, String>()

        try {
            java.io.StringReader(json).use { reader ->
                val jsonReader = com.google.gson.stream.JsonReader(reader)

                jsonReader.beginObject()

                while (jsonReader.hasNext()) {
                    val name = jsonReader.nextName()
                    if (name in fields) {
                        result[name] = jsonReader.nextString()
                    } else {
                        jsonReader.skipValue()
                    }
                }

                jsonReader.endObject()
                jsonReader.close()
            }
        } catch (e: Exception) {
            println("Error parsing fields: ${e.message}")
        }

        return result
    }
}

// 6. JSON Path Query (Simple)
class JsonPathQuery {

    private val gson: Gson = Gson()

    // Get nested value
    fun getNestedValue(json: String, path: String): String? {
        return try {
            val jsonObj = gson.fromJson(json, com.google.gson.JsonObject::class.java)
            val parts = path.split(".")

            var current: com.google.gson.JsonElement = jsonObj

            for (part in parts) {
                if (current.isJsonObject) {
                    current = current.asJsonObject.get(part)
                }
            }

            current.asString
        } catch (e: Exception) {
            println("Error getting nested value: ${e.message}")
            null
        }
    }

    // Check if field exists
    fun hasField(json: String, fieldName: String): Boolean {
        return try {
            val jsonObj = gson.fromJson(json, com.google.gson.JsonObject::class.java)
            jsonObj.has(fieldName)
        } catch (e: Exception) {
            false
        }
    }

    // Extract all values of a field from array
    fun extractFieldFromArray(json: String, fieldName: String): List<String> {
        val values = mutableListOf<String>()

        try {
            val jsonArray = gson.fromJson(json, com.google.gson.JsonArray::class.java)

            for (element in jsonArray) {
                val obj = element.asJsonObject
                if (obj.has(fieldName)) {
                    values.add(obj.get(fieldName).asString)
                }
            }
        } catch (e: Exception) {
            println("Error extracting field: ${e.message}")
        }

        return values
    }
}

// Main demonstration
fun demonstrateJsonDeserialization() {
    println("=== Android Kotlin JSON Deserialization Examples ===\n")

    // 1. Basic deserialization
    println("--- 1. Basic Deserialization ---")
    val basicDeserializer = BasicJsonDeserializer()

    val userJson = """{"id":1,"username":"alice","email":"[email protected]","age":25,"isActive":true}"""
    val user = basicDeserializer.deserializeObject(userJson, User::class.java)
    println("Deserialized user: $user")

    // 2. Pretty JSON
    println("\n--- 2. Pretty JSON ---")
    val prettyJson = """
        {
            "id": 2,
            "username": "bob",
            "email": "[email protected]",
            "age": 30,
            "isActive": true
        }
    """.trimIndent()

    val user2 = basicDeserializer.deserializePretty(prettyJson, User::class.java)
    println("Deserialized from pretty: $user2")

    // 3. Collection deserialization
    println("\n--- 3. Collection Deserialization ---")
    val collectionDeserializer = CollectionDeserializer()

    val usersJson = """[
        {"id":1,"username":"alice","email":"[email protected]"},
        {"id":2,"username":"bob","email":"[email protected]"},
        {"id":3,"username":"charlie","email":"[email protected]"}
    ]"""

    val users = collectionDeserializer.deserializeList(usersJson, User::class.java)
    println("Deserialized ${users.size} users:")
    users.forEach { println("  - ${it.username}") }

    // 4. Map deserialization
    val mapJson = """{"name":"Product","price":29.99,"inStock":true}"""
    val map = collectionDeserializer.deserializeMap(mapJson)
    println("\nDeserialized map: $map")

    // 5. Generic deserialization
    println("\n--- 5. Generic Type Deserialization ---")
    val advancedDeserializer = AdvancedJsonDeserializer()

    val apiResponseJson = """
        {
            "success": true,
            "data": {"id":1,"username":"alice","email":"[email protected]"},
            "message": "User found",
            "errorCode": null
        }
    """.trimIndent()

    val userType = object : TypeToken<User>() {}.type
    val response = advancedDeserializer.deserializeApiResponse<User>(apiResponseJson, userType)
    println("API Response: success=${response?.success}, data=${response?.data}")

    // 6. Safe deserialization
    println("\n--- 6. Safe Deserialization ---")
    val safeDeserializer = SafeJsonDeserializer()

    val validatedUser = safeDeserializer.deserializeWithValidation(
        userJson,
        User::class.java
    ) { user -> user.email.contains("@") }

    println("Validated user: $validatedUser")

    val fallbackUser = safeDeserializer.deserializeWithFallback(
        "{invalid json}",
        User::class.java,
        User(0, "fallback", "[email protected]")
    )
    println("Fallback user: $fallbackUser")

    // 7. JSON path
    println("\n--- 7. JSON Path Query ---")
    val pathQuery = JsonPathQuery()

    val complexJson = """
        {
            "user": {
                "id": 1,
                "profile": {
                    "username": "alice",
                    "email": "[email protected]"
                }
            }
        }
    """.trimIndent()

    val email = pathQuery.getNestedValue(complexJson, "user.profile.email")
    println("Extracted email: $email")

    val hasField = pathQuery.hasField(complexJson, "user")
    println("Has 'user' field: $hasField")

    println("\n=== All JSON Deserialization Examples Completed ===")
}

💻 Analyse XML kotlin

🟡 intermediate ⭐⭐⭐⭐

Analyser les documents XML en utilisant XmlPullParser et l'analyseur DOM

⏱️ 30 min 🏷️ kotlin, android, xml, parsing
Prerequisites: Intermediate Kotlin, XML basics
// Android Kotlin XML Parsing Examples
// Using XmlPullParser and DOM parser

import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserFactory
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import javax.xml.parsers.DocumentBuilderFactory
import java.io.StringReader

// Data classes for XML
data class Book(
    val id: Int,
    val title: String,
    val author: String,
    val price: Double,
    val genre: String?
)

data class User(
    val id: Int,
    val username: String,
    val email: String,
    val address: Address?
)

data class Address(
    val street: String,
    val city: String,
    val country: String
)

// 1. Basic XML Parsing with XmlPullParser
class BasicXmlParser {

    // Parse simple XML document
    fun parseSimpleXml(xml: String): List<Book> {
        val books = mutableListOf<Book>()
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var eventType = parser.eventType
        var currentId = 0
        var currentTitle = ""
        var currentAuthor = ""
        var currentPrice = 0.0
        var currentGenre: String? = null

        while (eventType != XmlPullParser.END_DOCUMENT) {
            when (eventType) {
                XmlPullParser.START_TAG -> {
                    when (parser.name) {
                        "book" -> {
                            currentId = parser.getAttributeValue(null, "id").toInt()
                        }
                        "title" -> {
                            currentTitle = parser.nextText()
                        }
                        "author" -> {
                            currentAuthor = parser.nextText()
                        }
                        "price" -> {
                            currentPrice = parser.nextText().toDouble()
                        }
                        "genre" -> {
                            currentGenre = parser.nextText()
                        }
                    }
                }
                XmlPullParser.END_TAG -> {
                    if (parser.name == "book") {
                        books.add(Book(currentId, currentTitle, currentAuthor, currentPrice, currentGenre))
                    }
                }
            }
            eventType = parser.next()
        }

        return books
    }

    // Parse XML with nested elements
    fun parseNestedXml(xml: String): List<User> {
        val users = mutableListOf<User>()
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var eventType = parser.eventType
        var currentId = 0
        var currentUsername = ""
        var currentEmail = ""
        var currentStreet = ""
        var currentCity = ""
        var currentCountry = ""
        var inAddress = false

        while (eventType != XmlPullParser.END_DOCUMENT) {
            when (eventType) {
                XmlPullParser.START_TAG -> {
                    when (parser.name) {
                        "user" -> {
                            currentId = parser.getAttributeValue(null, "id").toInt()
                        }
                        "username" -> currentUsername = parser.nextText()
                        "email" -> currentEmail = parser.nextText()
                        "address" -> inAddress = true
                        "street" -> if (inAddress) currentStreet = parser.nextText()
                        "city" -> if (inAddress) currentCity = parser.nextText()
                        "country" -> if (inAddress) currentCountry = parser.nextText()
                    }
                }
                XmlPullParser.END_TAG -> {
                    if (parser.name == "user") {
                        val address = if (currentStreet.isNotEmpty()) {
                            Address(currentStreet, currentCity, currentCountry)
                        } else null

                        users.add(User(currentId, currentUsername, currentEmail, address))

                        // Reset
                        currentStreet = ""
                        currentCity = ""
                        currentCountry = ""
                        inAddress = false
                    }
                }
            }
            eventType = parser.next()
        }

        return users
    }
}

// 2. DOM Parser
class DomXmlParser {

    // Parse XML using DOM
    fun parseWithDom(xml: String): List<Book> {
        val books = mutableListOf<Book>()

        try {
            val factory = DocumentBuilderFactory.newInstance()
            val builder = factory.newDocumentBuilder()
            val document = builder.parse(
                javax.xml.parsers.InputSource(StringReader(xml))
            )

            val bookNodes = document.getElementsByTagName("book")

            for (i in 0 until bookNodes.length) {
                val bookElement = bookNodes.item(i) as Element

                val id = bookElement.getAttribute("id").toInt()
                val title = getTextContent(bookElement, "title")
                val author = getTextContent(bookElement, "author")
                val price = getTextContent(bookElement, "price").toDouble()

                val genreNode = bookElement.getElementsByTagName("genre").item(0)
                val genre = if (genreNode != null) genreNode.textContent else null

                books.add(Book(id, title, author, price, genre))
            }

        } catch (e: Exception) {
            println("Error parsing with DOM: ${e.message}")
        }

        return books
    }

    // Get text content of child element
    private fun getTextContent(parent: Element, tagName: String): String {
        val nodes = parent.getElementsByTagName(tagName)
        return if (nodes.length > 0) {
            nodes.item(0).textContent
        } else {
            ""
        }
    }

    // Parse with attributes
    fun parseAttributes(xml: String): Map<String, Map<String, String>> {
        val result = mutableMapOf<String, Map<String, String>>()

        try {
            val factory = DocumentBuilderFactory.newInstance()
            val builder = factory.newDocumentBuilder()
            val document = builder.parse(
                javax.xml.parsers.InputSource(StringReader(xml))
            )

            val elements = document.documentElement.childNodes

            for (i in 0 until elements.length) {
                val node = elements.item(i)
                if (node.nodeType == Node.ELEMENT_NODE) {
                    val element = node as Element
                    val attrs = mutableMapOf<String, String>()

                    for (j in 0 until element.attributes.length) {
                        val attr = element.attributes.item(j)
                        attrs[attr.nodeName] = attr.nodeValue
                    }

                    result[element.tagName] = attrs
                }
            }

        } catch (e: Exception) {
            println("Error parsing attributes: ${e.message}")
        }

        return result
    }
}

// 3. XML to Object Mapper
class XmlObjectMapper {

    // Parse XML to generic map
    fun xmlToMap(xml: String): Map<String, Any> {
        val result = mutableMapOf<String, Any>()
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var eventType = parser.eventType
        var currentKey = ""
        val stack = mutableListOf<String>()

        while (eventType != XmlPullParser.END_DOCUMENT) {
            when (eventType) {
                XmlPullParser.START_TAG -> {
                    currentKey = parser.name
                    stack.add(currentKey)
                }
                XmlPullParser.TEXT -> {
                    val text = parser.text.trim()
                    if (text.isNotEmpty() && stack.size > 0) {
                        result[stack.joinToString(".")] = text
                    }
                }
                XmlPullParser.END_TAG -> {
                    if (stack.isNotEmpty()) {
                        stack.removeAt(stack.size - 1)
                    }
                }
            }
            eventType = parser.next()
        }

        return result
    }

    // Extract specific elements
    fun extractElements(xml: String, elementName: String): List<String> {
        val elements = mutableListOf<String>()
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var eventType = parser.eventType

        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG && parser.name == elementName) {
                elements.add(parser.nextText())
            }
            eventType = parser.next()
        }

        return elements
    }

    // Extract attributes
    fun extractAttributes(xml: String, elementName: String): List<Map<String, String>> {
        val attributesList = mutableListOf<Map<String, String>>()
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var eventType = parser.eventType

        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG && parser.name == elementName) {
                val attrs = mutableMapOf<String, String>()

                for (i in 0 until parser.attributeCount) {
                    attrs[parser.getAttributeName(i)] = parser.getAttributeValue(i)
                }

                attributesList.add(attrs)
            }
            eventType = parser.next()
        }

        return attributesList
    }
}

// 4. XML Validation and Utilities
class XmlUtilities {

    // Validate XML format
    fun isValidXml(xml: String): Boolean {
        return try {
            val factory = XmlPullParserFactory.newInstance()
            val parser = factory.newPullParser()
            parser.setInput(StringReader(xml))

            var eventType = parser.eventType
            while (eventType != XmlPullParser.END_DOCUMENT) {
                eventType = parser.next()
            }

            true
        } catch (e: Exception) {
            println("Invalid XML: ${e.message}")
            false
        }
    }

    // Format XML (pretty print)
    fun formatXml(xml: String): String {
        try {
            val factory = DocumentBuilderFactory.newInstance()
            val builder = factory.newDocumentBuilder()
            val document = builder.parse(
                javax.xml.parsers.InputSource(StringReader(xml))
            )

            // Use transformer for pretty printing
            val transformer = javax.xml.transform.TransformerFactory.newInstance().newTransformer()
            transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes")
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")

            val writer = java.io.StringWriter()
            transformer.transform(
                javax.xml.transform.dom.DOMSource(document),
                javax.xml.transform.stream.StreamResult(writer)
            )

            return writer.toString()
        } catch (e: Exception) {
            println("Error formatting XML: ${e.message}")
            return xml
        }
    }

    // Count elements
    fun countElements(xml: String, elementName: String): Int {
        val factory = XmlPullParserFactory.newInstance()
        val parser = factory.newPullParser()

        parser.setInput(StringReader(xml))

        var count = 0
        var eventType = parser.eventType

        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG && parser.name == elementName) {
                count++
            }
            eventType = parser.next()
        }

        return count
    }
}

// Main demonstration
fun demonstrateXmlParsing() {
    println("=== Android Kotlin XML Parsing Examples ===\n")

    // 1. Basic XML parsing
    println("--- 1. Basic XML Parsing ---")
    val basicParser = BasicXmlParser()

    val booksXml = """
        <catalog>
            <book id="1">
                <title>The Great Gatsby</title>
                <author>F. Scott Fitzgerald</author>
                <price>12.99</price>
                <genre>Fiction</genre>
            </book>
            <book id="2">
                <title>To Kill a Mockingbird</title>
                <author>Harper Lee</author>
                <price>14.99</price>
                <genre>Southern Gothic</genre>
            </book>
            <book id="3">
                <title>1984</title>
                <author>George Orwell</author>
                <price>11.99</price>
            </book>
        </catalog>
    """.trimIndent()

    val books = basicParser.parseSimpleXml(booksXml)
    println("Parsed ${books.size} books:")
    books.forEach { book ->
        println("  - ${book.title} by ${book.author} ($${book.price})")
    }

    // 2. Nested XML parsing
    println("\n--- 2. Nested XML Parsing ---")
    val usersXml = """
        <users>
            <user id="1">
                <username>alice</username>
                <email>[email protected]</email>
                <address>
                    <street>123 Main St</street>
                    <city>NYC</city>
                    <country>USA</country>
                </address>
            </user>
            <user id="2">
                <username>bob</username>
                <email>[email protected]</email>
            </user>
        </users>
    """.trimIndent()

    val users = basicParser.parseNestedXml(usersXml)
    println("Parsed ${users.size} users:")
    users.forEach { user ->
        println("  - ${user.username} (${user.email})")
        user.address?.let {
            println("    Address: ${it.street}, ${it.city}")
        }
    }

    // 3. DOM parsing
    println("\n--- 3. DOM Parsing ---")
    val domParser = DomXmlParser()
    val booksFromDom = domParser.parseWithDom(booksXml)
    println("Parsed via DOM: ${booksFromDom.size} books")

    // 4. XML to map
    println("\n--- 4. XML to Map ---")
    val mapper = XmlObjectMapper()
    val simpleXml = """
        <config>
            <app_name>MyApp</app_name>
            <version>1.0.0</version>
            <debug>true</debug>
        </config>
    """.trimIndent()

    val configMap = mapper.xmlToMap(simpleXml)
    println("Config map:")
    configMap.forEach { (key, value) ->
        println("  $key: $value")
    }

    // 5. Extract elements
    println("\n--- 5. Extract Elements ---")
    val titles = mapper.extractElements(booksXml, "title")
    println("Book titles: $titles")

    // 6. Extract attributes
    val bookAttrs = mapper.extractAttributes(booksXml, "book")
    println("\nBook attributes:")
    bookAttrs.forEach { attrs ->
        println("  Book ${attrs["id"]}")
    }

    // 7. Validation
    println("\n--- 6. Validation ---")
    val utilities = XmlUtilities()

    val isValid = utilities.isValidXml(booksXml)
    println("Is valid XML: $isValid")

    val elementCount = utilities.countElements(booksXml, "book")
    println("Book count: $elementCount")

    // 8. Format XML
    println("\n--- 7. Format XML ---")
    val unformattedXml = "<catalog><book id="1"><title>Test</title></book></catalog>"
    val formattedXml = utilities.formatXml(unformattedXml)
    println("Formatted XML:\n$formattedXml")

    println("\n=== All XML Parsing Examples Completed ===")
}