Ejemplos de Serialización macOS Swift

Ejemplos de serialización macOS Swift incluyendo serialización JSON, deserialización y análisis XML

💻 Serialización JSON swift

🟢 simple ⭐⭐

Serializar objetos Swift a JSON usando Codable y JSONSerialization

⏱️ 25 min 🏷️ swift, macos, serialization, json
Prerequisites: Basic Swift knowledge, Codable protocol
// macOS Swift JSON Serialization Examples
// Using Foundation framework

import Foundation

// 1. Basic JSON Serialization with Codable
struct User: Codable {
    let id: Int
    let username: String
    let email: String
    let age: Int
    let isActive: Bool
}

class BasicJSONSerialization {

    static func serializeUser() {
        print("\n--- Basic JSON Serialization ---")

        let user = User(
            id: 1,
            username: "john_doe",
            email: "[email protected]",
            age: 30,
            isActive: true
        )

        // Serialize to JSON
        do {
            let jsonData = try JSONEncoder().encode(user)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Serialized JSON:")
                print(jsonString)
            }

            // Also print as pretty JSON
            let json = try JSONSerialization.jsonObject(with: jsonData)
            let prettyData = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
            if let prettyString = String(data: prettyData, encoding: .utf8) {
                print("\nPretty printed:")
                print(prettyString)
            }

        } catch {
            print("Error serializing: \(error)")
        }
    }
}

// 2. JSON Serialization with Options
class JSONSerializationWithOptions {

    static void serializeWithOptions() {
        print("\n--- JSON Serialization with Options ---")

        let user = User(id: 1, username: "jane", email: "[email protected]", age: 25, isActive: false)

        // Custom encoder
        let encoder = JSONEncoder()

        // Pretty print
        encoder.outputFormatting = .prettyPrinted

        // Sorted keys
        encoder.outputFormatting.insert(.sortedKeys)

        // Without escaping slashes
        encoder.outputFormatting.insert(.withoutEscapingSlashes)

        do {
            let jsonData = try encoder.encode(user)
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("JSON with options:")
                print(jsonString)
            }

        } catch {
            print("Error: \(error)")
        }

        // Minimal encoding
        encoder.outputFormatting = []
        do {
            let jsonData = try encoder.encode(user)
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("\nMinimal JSON (no whitespace):")
                print(jsonString)
            }
        } catch {
            print("Error: \(error)")
        }
    }
}

// 3. Serialize Array of Objects
class SerializeArray {

    static void serializeArray() {
        print("\n--- Serialize Array of Objects ---")

        let users = [
            User(id: 1, username: "john", email: "[email protected]", age: 30, isActive: true),
            User(id: 2, username: "jane", email: "[email protected]", age: 25, isActive: true),
            User(id: 3, username: "bob", email: "[email protected]", age: 35, isActive: false)
        ]

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            let jsonData = try encoder.encode(users)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Serialized array:")
                print(jsonString)
            }

            // Get array size
            print("\nArray size: \(users.count) users")
            print("JSON size: \(jsonData.count) bytes")

        } catch {
            print("Error: \(error)")
        }
    }
}

// 4. Serialize Dictionary
class SerializeDictionary {

    static void serializeDictionary() {
        print("\n--- Serialize Dictionary ---")

        let userDict: [String: Any] = [
            "id": 1,
            "username": "john_doe",
            "email": "[email protected]",
            "age": 30,
            "isActive": true,
            "tags": ["admin", "user"],
            "metadata": [
                "created": "2024-01-15",
                "updated": "2024-01-20"
            ] as [String: Any]
        ]

        do {
            let jsonData = try JSONSerialization.data(withJSONObject: userDict, options: .prettyPrinted)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Serialized dictionary:")
                print(jsonString)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 5. Custom Key Encoding
struct Employee: Codable {
    let firstName: String
    let lastName: String
    let employeeId: Int

    enum CodingKeys: String, CodingKey {
        case firstName = "first_name"
        case lastName = "last_name"
        case employeeId = "employee_id"
    }
}

class CustomKeyEncoding {

    static void serializeWithCustomKeys() {
        print("\n--- Custom Key Encoding ---")

        let employee = Employee(
            firstName: "John",
            lastName: "Doe",
            employeeId: 12345
        )

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            let jsonData = try encoder.encode(employee)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Serialized with custom keys:")
                print(jsonString)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 6. Serialize with Date Encoding
struct Event: Codable {
    let name: String
    let date: Date
    let attendees: Int
}

class DateEncoding {

    static void serializeWithDate() {
        print("\n--- Serialize with Date Encoding ---")

        let event = Event(
            name: "Conference",
            date: Date(),
            attendees: 150
        )

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            // Default date encoding (seconds since 1970)
            encoder.dateEncodingStrategy = .deferredToDate

            let jsonData = try encoder.encode(event)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Default date encoding:")
                print(jsonString)
            }

            // ISO 8601 date encoding
            encoder.dateEncodingStrategy = .iso8601

            let isoData = try encoder.encode(event)

            if let isoString = String(data: isoData, encoding: .utf8) {
                print("\nISO 8601 date encoding:")
                print(isoString)
            }

            // Custom date formatting
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            encoder.dateEncodingStrategy = .formatted(formatter)

            let customData = try encoder.encode(event)

            if let customString = String(data: customData, encoding: .utf8) {
                print("\nCustom date format:")
                print(customString)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 7. Serialize Nested Objects
struct Address: Codable {
    let street: String
    let city: String
    let state: String
    let zipCode: String
}

struct Person: Codable {
    let name: String
    let age: Int
    let address: Address
    let phoneNumbers: [String]
}

class NestedSerialization {

    static void serializeNested() {
        print("\n--- Serialize Nested Objects ---")

        let person = Person(
            name: "John Doe",
            age: 30,
            address: Address(
                street: "123 Main St",
                city: "New York",
                state: "NY",
                zipCode: "10001"
            ),
            phoneNumbers: ["555-1234", "555-5678"]
        )

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            let jsonData = try encoder.encode(person)

            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("Serialized nested object:")
                print(jsonString)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 8. Conditional Encoding
struct Product: Codable {
    let id: Int
    let name: String
    let price: Double
    let discountPrice: Double?

    // Encode only if there's a discount
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(price, forKey: .price)

        if let discount = discountPrice {
            try container.encode(discount, forKey: .discountPrice)
        }
    }

    enum CodingKeys: String, CodingKey {
        case id, name, price, discountPrice
    }
}

class ConditionalEncoding {

    static void demonstrateConditionalEncoding() {
        print("\n--- Conditional Encoding ---")

        let productWithDiscount = Product(id: 1, name: "Widget", price: 99.99, discountPrice: 79.99)
        let productWithoutDiscount = Product(id: 2, name: "Gadget", price: 49.99, discountPrice: nil)

        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted

        do {
            let data1 = try encoder.encode(productWithDiscount)
            let data2 = try encoder.encode(productWithoutDiscount)

            print("Product with discount:")
            if let json1 = String(data: data1, encoding: .utf8) {
                print(json1)
            }

            print("\nProduct without discount:")
            if let json2 = String(data: data2, encoding: .utf8) {
                print(json2)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 9. Serialize to File
class SerializeToFile {

    static void serializeToFile() {
        print("\n--- Serialize to File ---")

        let user = User(id: 1, username: "john_doe", email: "[email protected]", age: 30, isActive: true)

        let filePath = "/tmp/user.json"

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            let jsonData = try encoder.encode(user)

            // Write to file
            try jsonData.write(to: URL(fileURLWithPath: filePath))

            print("JSON written to file: \(filePath)")

            // Read file size
            let attributes = try FileManager.default.attributesOfItem(atPath: filePath)
            if let fileSize = attributes[.size] as? UInt64 {
                print("File size: \(fileSize) bytes")
            }

            // Read and verify
            let readData = try Data(contentsOf: URL(fileURLWithPath: filePath))
            if let jsonString = String(data: readData, encoding: .utf8) {
                print("\nFile contents:")
                print(jsonString)
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// 10. Streaming JSON Serialization
class StreamingSerialization {

    static void streamArrayToJSON() {
        print("\n--- Streaming JSON Serialization ---")

        // Create large array
        let users = (1...100).map { i in
            User(id: i, username: "user\(i)", email: "user\(i)@test.com", age: 20 + i % 50, isActive: i % 2 == 0)
        }

        print("Streaming \(users.count) users to JSON...")

        do {
            let encoder = JSONEncoder()

            // Encode to data
            let startTime = Date()
            let jsonData = try encoder.encode(users)
            let elapsed = Date().timeIntervalSince(startTime)

            print("Encoded \(jsonData.count) bytes in \(elapsed) seconds")

            // Show first 500 characters
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("\nFirst 500 characters:")
                print(String(jsonString.prefix(500)))
            }

        } catch {
            print("Error: \(error)")
        }
    }

    static void writeStreamToFile(_ items: [User], filePath: String) {
        print("\n--- Write Stream to File ---")

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted

            let jsonData = try encoder.encode(items)
            try jsonData.write(to: URL(fileURLWithPath: filePath))

            print("Wrote \(items.count) items to \(filePath)")

            let attributes = try FileManager.default.attributesOfItem(atPath: filePath)
            if let fileSize = attributes[.size] as? UInt64 {
                print("File size: \(fileSize) bytes")
            }

        } catch {
            print("Error: \(error)")
        }
    }
}

// Main demonstration
func demonstrateJSONSerialization() {
    print("=== macOS Swift JSON Serialization Examples ===")

    BasicJSONSerialization.serializeUser()
    JSONSerializationWithOptions.serializeWithOptions()
    SerializeArray.serializeArray()
    SerializeDictionary.serializeDictionary()
    CustomKeyEncoding.serializeWithCustomKeys()
    DateEncoding.serializeWithDate()
    NestedSerialization.serializeNested()
    ConditionalEncoding.demonstrateConditionalEncoding()
    SerializeToFile.serializeToFile()
    StreamingSerialization.streamArrayToJSON()

    let users = (1...50).map { i in
        User(id: i, username: "stream\(i)", email: "stream\(i)@test.com", age: 20 + i, isActive: true)
    }
    StreamingSerialization.writeStreamToFile(users, filePath: "/tmp/users_stream.json")

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

// Run demonstration
demonstrateJSONSerialization()

💻 Deserialización JSON swift

🟡 intermediate ⭐⭐⭐

Deserializar datos JSON en objetos Swift usando Codable y JSONDecoder

⏱️ 30 min 🏷️ swift, macos, serialization, json
Prerequisites: Intermediate Swift, Codable protocol, Error handling
// macOS Swift JSON Deserialization Examples
// Using Foundation framework

import Foundation

// 1. Basic JSON Deserialization
struct User: Codable {
    let id: Int
    let username: String
    let email: String
    let age: Int
    let isActive: Bool
}

class BasicJSONDeserialization {

    static void deserializeUser() {
        print("\n--- Basic JSON Deserialization ---")

        let jsonString = """
        {
            "id": 1,
            "username": "john_doe",
            "email": "[email protected]",
            "age": 30,
            "isActive": true
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                let user = try JSONDecoder().decode(User.self, from: jsonData)

                print("Decoded user:")
                print("  ID: \(user.id)")
                print("  Username: \(user.username)")
                print("  Email: \(user.email)")
                print("  Age: \(user.age)")
                print("  Active: \(user.isActive)")

            } catch {
                print("Error decoding: \(error)")
            }
        }
    }
}

// 2. Deserialize Array
class DeserializeArray {

    static void deserializeArray() {
        print("\n--- Deserialize Array ---")

        let jsonArray = """
        [
            {
                "id": 1,
                "username": "john",
                "email": "[email protected]",
                "age": 30,
                "isActive": true
            },
            {
                "id": 2,
                "username": "jane",
                "email": "[email protected]",
                "age": 25,
                "isActive": true
            },
            {
                "id": 3,
                "username": "bob",
                "email": "[email protected]",
                "age": 35,
                "isActive": false
            }
        ]
        """

        if let jsonData = jsonArray.data(using: .utf8) {
            do {
                let users = try JSONDecoder().decode([User].self, from: jsonData)

                print("Decoded \(users.count) users:")
                for user in users {
                    print("  \(user.username) - \(user.email)")
                }

            } catch {
                print("Error decoding: \(error)")
            }
        }
    }
}

// 3. Deserialize from File
class DeserializeFromFile {

    static void deserializeFromFile() {
        print("\n--- Deserialize from File ---")

        // First, create a file
        let filePath = "/tmp/users_read.json"
        let users = [
            User(id: 1, username: "alice", email: "[email protected]", age: 28, isActive: true),
            User(id: 2, username: "bob", email: "[email protected]", age: 32, isActive: false)
        ]

        // Write to file first
        if let jsonData = try? JSONEncoder().encode(users) {
            try? jsonData.write(to: URL(fileURLWithPath: filePath))
            print("Created test file: \(filePath)")
        }

        // Now read from file
        if let jsonData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) {
            do {
                let decoder = JSONDecoder()
                let decodedUsers = try decoder.decode([User].self, from: jsonData)

                print("\nDecoded from file:")
                for user in decodedUsers {
                    print("  \(user.username) (\(user.age))")
                }

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 4. Custom Key Decoding
struct Employee: Codable {
    let firstName: String
    let lastName: String
    let employeeId: Int

    enum CodingKeys: String, CodingKey {
        case firstName = "first_name"
        case lastName = "last_name"
        case employeeId = "employee_id"
    }
}

class CustomKeyDecoding {

    static void deserializeWithCustomKeys() {
        print("\n--- Custom Key Decoding ---")

        let jsonString = """
        {
            "first_name": "John",
            "last_name": "Doe",
            "employee_id": 12345
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                let employee = try JSONDecoder().decode(Employee.self, from: jsonData)

                print("Decoded employee:")
                print("  First Name: \(employee.firstName)")
                print("  Last Name: \(employee.lastName)")
                print("  Employee ID: \(employee.employeeId)")

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 5. Date Decoding
struct Event: Codable {
    let name: String
    let date: Date
    let attendees: Int
}

class DateDecoding {

    static void deserializeWithDate() {
        print("\n--- Date Decoding ---")

        // Default Unix timestamp
        let unixJson = """
        {
            "name": "Conference",
            "date": 1705330800,
            "attendees": 150
        }
        """

        print("Decoding Unix timestamp:")
        if let jsonData = unixJson.data(using: .utf8) {
            do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .deferredToDate

                let event = try decoder.decode(Event.self, from: jsonData)

                let formatter = DateFormatter()
                formatter.dateStyle = .medium
                formatter.timeStyle = .short

                print("  Event: \(event.name)")
                print("  Date: \(formatter.string(from: event.date))")
                print("  Attendees: \(event.attendees)")

            } catch {
                print("Error: \(error)")
            }
        }

        // ISO 8601 format
        let isoJson = """
        {
            "name": "Meeting",
            "date": "2024-01-15T10:30:00Z",
            "attendees": 10
        }
        """

        print("\nDecoding ISO 8601:")
        if let jsonData = isoJson.data(using: .utf8) {
            do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .iso8601

                let event = try decoder.decode(Event.self, from: jsonData)

                let formatter = DateFormatter()
                formatter.dateStyle = .medium
                formatter.timeStyle = .short

                print("  Event: \(event.name)")
                print("  Date: \(formatter.string(from: event.date))")

            } catch {
                print("Error: \(error)")
            }
        }

        // Custom date format
        let customJson = """
        {
            "name": "Party",
            "date": "2024-01-15 22:00:00",
            "attendees": 50
        }
        """

        print("\nDecoding custom date format:")
        if let jsonData = customJson.data(using: .utf8) {
            do {
                let decoder = JSONDecoder()

                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                decoder.dateDecodingStrategy = .formatted(formatter)

                let event = try decoder.decode(Event.self, from: jsonData)

                print("  Event: \(event.name)")
                print("  Date: \(event.date)")

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 6. Nested Object Decoding
struct Address: Codable {
    let street: String
    let city: String
    let state: String
    let zipCode: String
}

struct Person: Codable {
    let name: String
    let age: Int
    let address: Address
    let phoneNumbers: [String]
}

class NestedDecoding {

    static void deserializeNested() {
        print("\n--- Nested Object Decoding ---")

        let jsonString = """
        {
            "name": "John Doe",
            "age": 30,
            "address": {
                "street": "123 Main St",
                "city": "New York",
                "state": "NY",
                "zipCode": "10001"
            },
            "phoneNumbers": ["555-1234", "555-5678"]
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                let person = try JSONDecoder().decode(Person.self, from: jsonData)

                print("Decoded person:")
                print("  Name: \(person.name)")
                print("  Age: \(person.age)")
                print("  Address:")
                print("    Street: \(person.address.street)")
                print("    City: \(person.address.city)")
                print("    State: \(person.address.state)")
                print("    ZIP: \(person.address.zipCode)")
                print("  Phone numbers: \(person.phoneNumbers.joined(separator: ", "))")

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 7. Handling Missing Fields
struct OptionalUser: Codable {
    let id: Int
    let username: String
    let email: String?
    let age: Int?
    let bio: String?
}

class OptionalFieldsDecoding {

    static void deserializeOptionalFields() {
        print("\n--- Handling Missing/Optional Fields ---")

        let jsonString = """
        {
            "id": 1,
            "username": "john_doe"
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                let user = try JSONDecoder().decode(OptionalUser.self, from: jsonData)

                print("Decoded user with optional fields:")
                print("  ID: \(user.id)")
                print("  Username: \(user.username)")
                print("  Email: \(user.email ?? "Not provided")")
                print("  Age: \(user.age?.description ?? "Not provided")")
                print("  Bio: \(user.bio ?? "Not provided")")

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 8. Error Handling
class ErrorHandling {

    static void handleDecodingErrors() {
        print("\n--- Decoding Error Handling ---")

        // Invalid JSON
        let invalidJson = "{ invalid json }"

        print("1. Invalid JSON:")
        if let jsonData = invalidJson.data(using: .utf8) {
            do {
                _ = try JSONDecoder().decode(User.self, from: jsonData)
            } catch let DecodingError.dataCorrupted(context) {
                print("  Data corrupted: \(context)")
            } catch let DecodingError.keyNotFound(key, context) {
                print("  Key not found: \(key) - \(context)")
            } catch let DecodingError.typeMismatch(type, context) {
                print("  Type mismatch: \(type) - \(context)")
            } catch let DecodingError.valueNotFound(type, context) {
                print("  Value not found: \(type) - \(context)")
            } catch {
                print("  Other error: \(error)")
            }
        }

        // Missing required field
        let incompleteJson = """
        {
            "id": 1,
            "username": "john"
        }
        """

        print("\n2. Missing required field:")
        if let jsonData = incompleteJson.data(using: .utf8) {
            do {
                _ = try JSONDecoder().decode(User.self, from: jsonData)
            } catch {
                print("  Error: \(error.localizedDescription)")
            }
        }

        // Wrong type
        let wrongTypeJson = """
        {
            "id": "not_a_number",
            "username": "john",
            "email": "[email protected]",
            "age": 30,
            "isActive": true
        }
        """

        print("\n3. Wrong type:")
        if let jsonData = wrongTypeJson.data(using: .utf8) {
            do {
                _ = try JSONDecoder().decode(User.self, from: jsonData)
            } catch {
                print("  Error: \(error.localizedDescription)")
            }
        }
    }
}

// 9. Partial Decoding
class PartialDecoding {

    static void partialDecode() {
        print("\n--- Partial Decoding ---")

        let jsonString = """
        {
            "id": 1,
            "username": "john_doe",
            "email": "[email protected]",
            "age": 30,
            "isActive": true,
            "metadata": {
                "created": "2024-01-15",
                "updated": "2024-01-20"
            },
            "settings": {
                "theme": "dark",
                "notifications": true
            }
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                // Decode only the User part
                let user = try JSONDecoder().decode(User.self, from: jsonData)

                print("Partially decoded user:")
                print("  Username: \(user.username)")
                print("  Email: \(user.email)")

                // Decode entire JSON to dictionary
                if let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] {
                    print("\nFull JSON available for other fields:")
                    if let metadata = json["metadata"] as? [String: String] {
                        print("  Created: \(metadata["created"] ?? "")")
                        print("  Updated: \(metadata["updated"] ?? "")")
                    }
                }

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 10. Streaming JSON Parsing
class StreamingParsing {

    static void streamJSONArray() {
        print("\n--- Streaming JSON Parsing ---")

        // Simulate large JSON array
        let largeArray = (1...1000).map { i in
            [
                "id": i,
                "username": "user\(i)",
                "email": "user\(i)@test.com",
                "age": 20 + i % 50,
                "isActive": i % 2 == 0
            ] as [String: Any]
        }

        guard let jsonData = try? JSONSerialization.data(withJSONObject: largeArray) else {
            return
        }

        print("Parsing \(jsonData.count) bytes of JSON...")

        let startTime = Date()

        do {
            let users = try JSONDecoder().decode([User].self, from: jsonData)

            let elapsed = Date().timeIntervalSince(startTime)

            print("Parsed \(users.count) users in \(elapsed) seconds")
            print("First user: \(users.first?.username ?? "")")
            print("Last user: \(users.last?.username ?? "")")

        } catch {
            print("Error: \(error)")
        }
    }

    static void parseChunkedJSON() {
        print("\n--- Parse Chunked JSON ---")

        let jsonString = """
        {"users": [
            {"id": 1, "username": "john", "email": "[email protected]", "age": 30, "isActive": true},
            {"id": 2, "username": "jane", "email": "[email protected]", "age": 25, "isActive": true},
            {"id": 3, "username": "bob", "email": "[email protected]", "age": 35, "isActive": false}
        ]}
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                let decoder = JSONDecoder()

                // Parse as dictionary first
                if let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
                   let usersArray = json["users"] as? [[String: Any]] {

                    print("Parsed \(usersArray.count) users")

                    // Then decode each user
                    for userDict in usersArray {
                        if let userData = try? JSONSerialization.data(withJSONObject: userDict),
                           let user = try? decoder.decode(User.self, from: userData) {
                            print("  \(user.username)")
                        }
                    }
                }

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// 11. Dynamic Decoding
class DynamicDecoding {

    static void decodeDynamicJSON() {
        print("\n--- Dynamic JSON Decoding ---")

        let jsonString = """
        {
            "type": "user",
            "data": {
                "id": 1,
                "username": "john_doe",
                "email": "[email protected]",
                "age": 30,
                "isActive": true
            }
        }
        """

        if let jsonData = jsonString.data(using: .utf8) {
            do {
                if let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
                   let type = json["type"] as? String,
                   let data = json["data"] as? [String: Any] {

                    print("Type: \(type)")

                    // Decode based on type
                    if type == "user" {
                        let userData = try JSONSerialization.data(withJSONObject: data)
                        let user = try JSONDecoder().decode(User.self, from: userData)

                        print("User: \(user.username)")
                    }
                }

            } catch {
                print("Error: \(error)")
            }
        }
    }
}

// Main demonstration
func demonstrateJSONDeserialization() {
    print("=== macOS Swift JSON Deserialization Examples ===")

    BasicJSONDeserialization.deserializeUser()
    DeserializeArray.deserializeArray()
    DeserializeFromFile.deserializeFromFile()
    CustomKeyDecoding.deserializeWithCustomKeys()
    DateDecoding.deserializeWithDate()
    NestedDecoding.deserializeNested()
    OptionalFieldsDecoding.deserializeOptionalFields()
    ErrorHandling.handleDecodingErrors()
    PartialDecoding.partialDecode()
    StreamingParsing.streamJSONArray()
    StreamingParsing.parseChunkedJSON()
    DynamicDecoding.decodeDynamicJSON()

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

// Run demonstration
demonstrateJSONDeserialization()

💻 Análisis XML swift

🟡 intermediate ⭐⭐⭐

Analizar documentos XML usando XMLParser y convertir a objetos Swift

⏱️ 35 min 🏷️ swift, macos, serialization, xml
Prerequisites: Intermediate Swift, XMLParser, Delegate pattern
// macOS Swift XML Parsing Examples
// Using Foundation framework

import Foundation

// 1. Basic XML Parsing
class BasicXMLParser: NSObject, XMLParserDelegate {
    var currentElement: String = ""
    var foundCharacters: String = ""

    func parseXML(xmlString: String) {
        print("\n--- Basic XML Parsing ---")

        guard let data = xmlString.data(using: .utf8) else {
            print("Invalid XML string")
            return
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        print("Parsing XML:")
        if parser.parse() {
            print("XML parsed successfully")
        } else {
            print("XML parsing failed: \(parser.parserError?.localizedDescription ?? "")")
        }
    }

    // Delegate methods
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentElement = elementName
        foundCharacters = ""

        print("Started element: \(elementName)")

        if !attributeDict.isEmpty {
            print("  Attributes: \(attributeDict)")
        }
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        foundCharacters += string
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        let trimmed = foundCharacters.trimmingCharacters(in: .whitespacesAndNewlines)

        if !trimmed.isEmpty {
            print("  \(elementName): \(trimmed)")
        }

        currentElement = ""
    }

    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
        print("Parse error: \(parseError)")
    }
}

// 2. Parse XML to Object
struct Book {
    let title: String
    let author: String
    let year: String
    let price: Double
}

class BookParser: NSObject, XMLParserDelegate {
    var books: [Book] = []
    var currentElement: String = ""
    var currentTitle: String = ""
    var currentAuthor: String = ""
    var currentYear: String = ""
    var currentPrice: String = ""

    func parseBooks(xmlString: String) -> [Book] {
        print("\n--- Parse XML to Objects ---")

        guard let data = xmlString.data(using: .utf8) else {
            return []
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()

        print("Parsed \(books.count) books")

        return books
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentElement = elementName
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
        if !trimmed.isEmpty {
            switch currentElement {
            case "title":
                currentTitle = trimmed
            case "author":
                currentAuthor = trimmed
            case "year":
                currentYear = trimmed
            case "price":
                currentPrice = trimmed
            default:
                break
            }
        }
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "book" {
            let price = Double(currentPrice) ?? 0.0
            let book = Book(title: currentTitle, author: currentAuthor, year: currentYear, price: price)
            books.append(book)

            print("  Book: \(book.title) by \(book.author) - $\(book.price)")

            // Reset
            currentTitle = ""
            currentAuthor = ""
            currentYear = ""
            currentPrice = ""
        }
    }
}

// 3. Parse XML with Attributes
struct Product {
    let id: String
    let category: String
    let name: String
    let price: Double
}

class ProductParser: NSObject, XMLParserDelegate {
    var products: [Product] = []
    var currentElement: String = ""
    var currentId: String = ""
    var currentCategory: String = ""
    var currentName: String = ""
    var currentPrice: String = ""

    func parseProducts(xmlString: String) -> [Product] {
        print("\n--- Parse XML with Attributes ---")

        guard let data = xmlString.data(using: .utf8) else {
            return []
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()

        print("Parsed \(products.count) products")

        return products
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentElement = elementName

        if elementName == "product" {
            currentId = attributeDict["id"] ?? ""
            currentCategory = attributeDict["category"] ?? ""
            print("  Found product: id=\(currentId), category=\(currentCategory)")
        }
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
        if !trimmed.isEmpty {
            switch currentElement {
            case "name":
                currentName = trimmed
            case "price":
                currentPrice = trimmed
            default:
                break
            }
        }
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "product" {
            let price = Double(currentPrice) ?? 0.0
            let product = Product(id: currentId, category: currentCategory, name: currentName, price: price)
            products.append(product)

            // Reset
            currentId = ""
            currentCategory = ""
            currentName = ""
            currentPrice = ""
        }
    }
}

// 4. Parse Nested XML
struct Address {
    let street: String
    let city: String
    let state: String
    let zip: String
}

struct Person {
    let name: String
    let age: Int
    let address: Address
}

class PersonParser: NSObject, XMLParserDelegate {
    var people: [Person] = []
    var currentElement: String = ""
    var currentName: String = ""
    var currentAge: String = ""
    var currentStreet: String = ""
    var currentCity: String = ""
    var currentState: String = ""
    var currentZip: String = ""
    var inAddress = false

    func parsePeople(xmlString: String) -> [Person] {
        print("\n--- Parse Nested XML ---")

        guard let data = xmlString.data(using: .utf8) else {
            return []
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()

        print("Parsed \(people.count) people")

        return people
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentElement = elementName

        if elementName == "address" {
            inAddress = true
        }
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
        if !trimmed.isEmpty {
            if inAddress {
                switch currentElement {
                case "street":
                    currentStreet = trimmed
                case "city":
                    currentCity = trimmed
                case "state":
                    currentState = trimmed
                case "zip":
                    currentZip = trimmed
                default:
                    break
                }
            } else {
                switch currentElement {
                case "name":
                    currentName = trimmed
                case "age":
                    currentAge = trimmed
                default:
                    break
                }
            }
        }
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "address" {
            inAddress = false
        }

        if elementName == "person" {
            let age = Int(currentAge) ?? 0
            let address = Address(street: currentStreet, city: currentCity, state: currentState, zip: currentZip)
            let person = Person(name: currentName, age: age, address: address)
            people.append(person)

            print("  Person: \(person.name) from \(person.address.city)")

            // Reset
            currentName = ""
            currentAge = ""
            currentStreet = ""
            currentCity = ""
            currentState = ""
            currentZip = ""
        }
    }
}

// 5. Parse XML from File
class FileXMLParser {

    static void parseXMLFromFile(filePath: String) {
        print("\n--- Parse XML from File ---")

        // First, create a sample XML file
        let sampleXML = """
        <?xml version="1.0" encoding="UTF-8"?>
        <catalog>
            <book>
                <title>Swift Programming</title>
                <author>John Smith</author>
                <year>2024</year>
                <price>49.99</price>
            </book>
            <book>
                <title>iOS Development</title>
                <author>Jane Doe</author>
                <year>2023</year>
                <price>39.99</price>
            </book>
        </catalog>
        """

        try? sampleXML.write(to: URL(fileURLWithPath: filePath), atomically: true, encoding: .utf8)
        print("Created sample XML file: \(filePath)")

        // Parse from file
        guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
            print("Could not read file")
            return
        }

        let parser = XMLParser(data: data)
        let bookParser = BookParser()
        parser.delegate = bookParser

        if parser.parse() {
            print("\nSuccessfully parsed from file")
        }
    }
}

// 6. Parse XML with Namespaces
class NamespaceParser: NSObject, XMLParserDelegate {

    func parseWithNamespaces(xmlString: String) {
        print("\n--- Parse XML with Namespaces ---")

        guard let data = xmlString.data(using: .utf8) else {
            return
        }

        let parser = XMLParser(data: data)
        parser.delegate = self
        parser.shouldProcessNamespaces = true

        parser.parse()
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        print("Element: \(elementName)")

        if let uri = namespaceURI {
            print("  Namespace URI: \(uri)")
        }

        if let qname = qName {
            print("  Qualified name: \(qname)")
        }
    }

    func parser(_ parser: XMLParser, didStartMappingPrefix prefix: String, toURI namespaceURI: String) {
        print("Namespace mapping: \(prefix) -> \(namespaceURI)")
    }
}

// 7. Parse Large XML with Streaming
class StreamingXMLParser: NSObject, XMLParserDelegate {
    var elementCount = 0
    var maxElements = 0

    func parseStreaming(xmlString: String, maxElements: Int = 10) {
        print("\n--- Streaming XML Parsing ---")

        self.maxElements = maxElements
        elementCount = 0

        guard let data = xmlString.data(using: .utf8) else {
            return
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()

        print("Processed \(elementCount) elements (stopped at \(maxElements))")
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        elementCount += 1

        if elementCount <= maxElements {
            print("  Element \(elementCount): \(elementName)")
        }

        if elementCount >= maxElements {
            parser.abortParsing()
        }
    }
}

// 8. Parse XML with CDATA
class CDATAParser: NSObject, XMLParserDelegate {

    func parseWithCDATA(xmlString: String) {
        print("\n--- Parse XML with CDATA ---")

        guard let data = xmlString.data(using: .utf8) else {
            return
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()
    }

    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
        if let cdataString = String(data: CDATABlock, encoding: .utf8) {
            print("Found CDATA:")
            print("  \(cdataString)")
        }
    }
}

// 9. XML to Dictionary Conversion
class XMLToDictionary {

    static void convertToDictionary(xmlString: String) -> [String: Any]? {
        print("\n--- XML to Dictionary Conversion ---")

        guard let data = xmlString.data(using: .utf8) else {
            return nil
        }

        do {
            let plist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil)

            if let dict = plist as? [String: Any] {
                print("Converted to dictionary:")
                print(dict)
                return dict
            }

        } catch {
            print("Error: \(error)")
        }

        return nil
    }
}

// 10. Parse RSS Feed
struct RSSItem {
    let title: String
    let link: String
    let description: String
    let pubDate: String
}

class RSSParser: NSObject, XMLParserDelegate {
    var items: [RSSItem] = []
    var currentElement: String = ""
    var currentTitle: String = ""
    var currentLink: String = ""
    var currentDescription: String = ""
    var currentPubDate: String = ""
    var inItem = false

    func parseRSS(xmlString: String) -> [RSSItem] {
        print("\n--- Parse RSS Feed ---")

        guard let data = xmlString.data(using: .utf8) else {
            return []
        }

        let parser = XMLParser(data: data)
        parser.delegate = self

        parser.parse()

        print("Parsed \(items.count) RSS items")

        return items
    }

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
        currentElement = elementName

        if elementName == "item" {
            inItem = true
        }
    }

    func parser(_ parser: XMLParser, foundCharacters string: String) {
        let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)

        if !trimmed.isEmpty && inItem {
            switch currentElement {
            case "title":
                currentTitle += trimmed
            case "link":
                currentLink += trimmed
            case "description":
                currentDescription += trimmed
            case "pubDate":
                currentPubDate += trimmed
            default:
                break
            }
        }
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        if elementName == "item" {
            let item = RSSItem(
                title: currentTitle,
                link: currentLink,
                description: currentDescription,
                pubDate: currentPubDate
            )
            items.append(item)

            print("  RSS Item: \(item.title)")

            // Reset
            currentTitle = ""
            currentLink = ""
            currentDescription = ""
            currentPubDate = ""
            inItem = false
        }
    }
}

// Main demonstration
func demonstrateXMLParsing() {
    print("=== macOS Swift XML Parsing Examples ===")

    // 1. Basic XML
    let basicXML = """
    <root>
        <greeting>Hello, World!</greeting>
        <message>Welcome to Swift XML parsing</message>
    </root>
    """

    let basicParser = BasicXMLParser()
    basicParser.parseXML(xmlString: basicXML)

    // 2. Books XML
    let booksXML = """
    <catalog>
        <book>
            <title>Swift Programming</title>
            <author>John Smith</author>
            <year>2024</year>
            <price>49.99</price>
        </book>
        <book>
            <title>iOS Development</title>
            <author>Jane Doe</author>
            <year>2023</year>
            <price>39.99</price>
        </book>
    </catalog>
    """

    let bookParser = BookParser()
    bookParser.parseBooks(xmlString: booksXML)

    // 3. Products with attributes
    let productsXML = """
    <products>
        <product id="1" category="electronics">
            <name>Laptop</name>
            <price>999.99</price>
        </product>
        <product id="2" category="books">
            <name>Programming Guide</name>
            <price>29.99</price>
        </product>
    </products>
    """

    let productParser = ProductParser()
    productParser.parseProducts(xmlString: productsXML)

    // 4. Nested XML
    let peopleXML = """
    <people>
        <person>
            <name>John Doe</name>
            <age>30</age>
            <address>
                <street>123 Main St</street>
                <city>New York</city>
                <state>NY</state>
                <zip>10001</zip>
            </address>
        </person>
        <person>
            <name>Jane Smith</name>
            <age>25</age>
            <address>
                <street>456 Oak Ave</street>
                <city>Los Angeles</city>
                <state>CA</state>
                <zip>90001</zip>
            </address>
        </person>
    </people>
    """

    let personParser = PersonParser()
    personParser.parsePeople(xmlString: peopleXML)

    // 5. Parse from file
    FileXMLParser.parseXMLFromFile(filePath: "/tmp/books.xml")

    // 6. RSS Feed
    let rssXML = """
    <rss>
        <channel>
            <title>Swift News</title>
            <item>
                <title>Swift 5.9 Released</title>
                <link>https://swift.org/blog/swift-5.9</link>
                <description>The latest version of Swift brings new features</description>
                <pubDate>Mon, 15 Jan 2024 10:00:00 GMT</pubDate>
            </item>
            <item>
                <title>iOS 17 Preview</title>
                <link>https://apple.com/ios17</link>
                <description>Explore the new features of iOS 17</description>
                <pubDate>Tue, 16 Jan 2024 14:30:00 GMT</pubDate>
            </item>
        </channel>
    </rss>
    """

    let rssParser = RSSParser()
    rssParser.parseRSS(xmlString: rssXML)

    // 7. Streaming XML
    let largeXML = """
    <root>
        \((1...20).map { "<item>Item \($0)</item>" }.joined(separator: "\n"))
    </root>
    """

    let streamingParser = StreamingXMLParser()
    streamingParser.parseStreaming(xmlString: largeXML, maxElements: 5)

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

// Run demonstration
demonstrateXMLParsing()