xml - Implementando un decodificador personalizado en Swift 4
decoder codable (1)
Me gustaría decodificar un documento XML utilizando el nuevo protocolo Decodable
introducido en Swift 4, sin embargo, no parece haber una implementación existente para un decodificador XML que se ajuste al protocolo Decoder
.
Mi plan era usar la biblioteca SWXMLHash para analizar el XML y luego posiblemente hacer que la clase XMLIndexer
en esa biblioteca extienda el protocolo Decoder
para que mi modelo pueda inicializarse con una instancia de XMLIndexer
( XMLIndexer
es devuelto por SWXMLHash.parse(xmlString)
) .
Mi problema es que no tengo ni idea de cómo implementar el protocolo Decoder
y parece que no puedo encontrar ningún recurso en línea que explique cómo se hace. Cada recurso que he encontrado menciona estrictamente la clase JSONDecoder
que se incluye con la biblioteca estándar de Swift y ningún recurso que he encontrado aborda el problema de crear su propio decodificador personalizado.
Todavía no he tenido la oportunidad de convertir mi código en un marco, pero puedes echar un vistazo a mi repositorio Github que implementa un decodificador y codificador personalizados para XML.
Enlace: https://github.com/ShawnMoore/XMLParsing
El codificador y el decodificador residen en la carpeta XML del repositorio. Se basa en JSONEncoder y JSONDecoder de Apple con cambios que se ajustan al estándar XML.
Diferencias entre XMLDecoder y JSONDecoder
-
XMLDecoder.DateDecodingStrategy
tiene un caso adicional tituladokeyFormatted
. Este caso requiere un cierre que le proporciona una CodingKey, y depende de usted proporcionar el DateFormatter correcto para la clave proporcionada. Esto es simplemente un caso de conveniencia en la estrategia DateDecoding de JSONDecoder. -
XMLDecoder.DataDecodingStrategy
tiene un caso adicional tituladokeyFormatted
. Este caso requiere un cierre que le proporciona un CodingKey, y depende de usted proporcionar los datos correctos o nula para la clave proporcionada. Este es simplemente un caso de conveniencia en la estrategia de DataDecoding de JSONDecoder. - Si el objeto que cumple con el protocolo Codable tiene una matriz, y el XML que se está analizando no contiene el elemento de la matriz, XMLDecoder asignará una matriz vacía al atributo. Esto se debe a que el estándar XML dice que si el XML no contiene el atributo, eso podría significar que hay cero de esos elementos.
Diferencias entre XMLEncoder y JSONEncoder
Contiene una opción llamada
StringEncodingStrategy
, esta enumeración tiene dos opciones,deferredToString
ycdata
. La opción deferredToString es predeterminada y codificará cadenas como cadenas simples. Si se selecciona cdata , todas las cadenas serán codificadas como CData.La función de
encode
toma dos parámetros adicionales que JSONEncoder. El primer parámetro adicional en la función es una cadena RootKey que tendrá todo el XML envuelto en un elemento llamado esa clave. Este parámetro es obligatorio. El segundo parámetro es un XMLHeader, que es un parámetro opcional que puede tomar la versión, la estrategia de codificación y el estado independiente, si desea incluir esta información en el código XML codificado.
Ejemplos
Para obtener una lista completa de ejemplos, consulte la carpeta XML de muestra en el repositorio.
XML a analizar:
<?xml version="1.0"?>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer''s Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
Estructuras Swift:
struct Book: Codable {
var id: String
var author: String
var title: String
var genre: Genre
var price: Double
var publishDate: Date
var description: String
enum CodingKeys: String, CodingKey {
case id, author, title, genre, price, description
case publishDate = "publish_date"
}
}
enum Genre: String, Codable {
case computer = "Computer"
case fantasy = "Fantasy"
case romance = "Romance"
case horror = "Horror"
case sciFi = "Science Fiction"
}
XMLDecoder:
let data = Data(forResource: "book", withExtension: "xml") else { return nil }
let decoder = XMLDecoder()
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let book = try decoder.decode(Book.self, from: data)
} catch {
print(error)
}
XMLEncoder:
let encoder = XMLEncoder()
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
encoder.dateEncodingStrategy = .formatted(formatter)
do {
let data = try encoder.encode(self, withRootKey: "book", header: XMLHeader(version: 1.0))
print(String(data: data, encoding: .utf8))
} catch {
print(error)
}