2011-05-26 18 views
12

Estoy intentando construir un analizador/objetoMapper que construirá objetos de Objective C para el JSON que consumo de un servicio REST.Biblioteca de asignación de objetos de JSON a NSObjects

Me inspiré en RestKit al hacer que todas mis entidades tengan una "lista de decodificación" que le dice a un asignador qué claves JSON corresponden a cada objeto. De esta manera:

//ObjectEntity implementation 
+ (NSDictionary*) mapProperties { 

    /* 
    localPropertiy - JSONProperty 
    */ 

    return @{ 
      @"name": @"name", 
      @"category": @"category", 
      @"possible_scopes": @"possibleScopes", 
      @"possible_descriptions": @"possibleDescriptions", 
      @"key": @"keys"    
    }; 

} 

+ (NSDictionary*) mapRelations { 

    return [NSDictionary dictionary]; 
} 

lo hice porque me gusta la encapsulación de estos valores cambiables para estar en el objeto que hacen referencia. Haciendo que el Mapper sepa lo menos posible.

El asignador hace algo como esto:

+ (NSArray*) parseData:(NSData*) jsonData intoObjectsOfType:(Class) objectClass { 

    //Parser result from web service 
    NSError *error = nil; 
    CJSONDeserializer *deserializer = [CJSONDeserializer deserializer]; 
    [deserializer setNullObject:nil]; 
    NSArray *objects = [deserializer deserializeAsArray:jsonData error:&error]; 

    NSMutableArray *result = [NSMutableArray array]; 

    for (NSDictionary *o in objects) { 

     id <EntityProtocol> entity = [[objectClass alloc] init]; 

     NSDictionary *jsonKeys = objectClass.mapProperties; 

     for (NSString *key in jsonKeys.allKeys) { 

      NSString *objectProperty = jsonKeys[key]; 
      NSString *value = o[key]; 
      if (value) 
       [entity setValue:value forKey:objectProperty]; 
     } 

     [result addObject:entity]; 

    } 

    return (NSArray*)result; 
} 

Así que el mensaje del analizador/mapeador de la siguiente manera:

NSArray *objects = [ObjectParser parseData:self.responseData intoObjectsOfType:ObjectEntity.class]; 

Esto significa que el analizador debe saber lo que mi objeto raíz es, que es bien como el objeto que lo recupera del servicio web, por supuesto, tiene este conocimiento.

Lo anterior sólo funciona para JSON sin objetos anidados, que han estado tratando de construir el analizador de manera que tomará las relaciones en cuenta también, la construcción de los objetos necesarios e insertándolos en el objeto raíz, esto tiene que ser recursivo y sigo corriendo en callejones sin salida.

Me gustaría obtener alguna ayuda sobre cómo podría abordar esto o cualquier idea de si algo como esto existe como una biblioteca. Tal vez para usar o tal vez solo para abordar las partes con las que tengo problemas.

Gracias de antemano.

+1

+1 porque estás haciendo algo genial! –

+0

Gracias Alec, parece que las cosas geniales son bastante difíciles de entender en estos días. – RickiG

+0

Tenemos una configuración bastante compleja para enviar/recibir JSON, pero la esencia es que todas las clases habilitadas para JSON tienen un método 'initWithDict' y un método' asDictionary'. Cada clase es responsable de leer/escribir los datos que "posee", y a su vez llama a los mismos métodos para las clases de componentes. –

Respuesta

2

¿Por qué no agregar asignaciones para las clases?

+ (NSDictionary *)mapClasses { 
    return @{ 
      @"category": [Category class], 
      // ... 
    }; 
} 

Para las propiedades no-contenedores, incluso se podría hacer para evitar asignaciones runtime introspection of properties redundantes.

propiedades contenedores pueden corresponderse con objetos envolventes especiales:

[OMContainer arrayWithClass:Key.class], @"keys", 
[OMContainer dicionaryWithKeyClass:ScopeID.class valueClass:Scope.class], @"possibleScopes", 

O incluso bloques para la selección dinámica de tipos:

[OMDynamicType typeWithBlock:^(id obj){ 
    if ([obj isKindOfClass:NSString.class] && [obj hasPrefix:@"foo"]) 
     return Foo.class; 
    else 
     return Bar.class; 
}], @"foo", 

La implementación de este podría ser algo como:

+ (NSArray *)parseData:(NSData*)jsonData intoObjectsOfType:(Class)objectClass { 
    NSArray *parsed = /* ... */ 
    NSMutableArray *decoded = [NSMutableArray array]; 
    for (id obj in parsed) { 
     [decoded addObject:[self decodeRawObject:parsed intoObjectOfType:objectClass]]; 
    } 
    return decoded; 
} 

+ (id)decodeRawObject:(NSDictionary *)dict intoObjectOfType:(Class)objectClass { 
    // ... 
    NSDictionary *jsonKeys = objectClass.mapProperties; 
    NSDictionary *jsonClasses = objectClass.mapClasses; 

    for (NSString *key in jsonKeys.allKeys) { 
     NSString *objectProperty = jsonKeys[key]; 
     NSString *value = dict[key]; 
     if (value) { 
      id klass = jsonClasses[key]; 
      if (!klass) { 
       [entity setValue:value forKey:objectProperty]; 
      } else if (klass == klass.class) { 
       [entity setValue:[self decodeRawObject:value intoObjectOfType:klass] 
          forKey:objectProperty]; 
      } else if (/* check for containers and blocks */) { 
       // ... 
      } 
     } 
    } 
    // ... 
} 
+0

Gracias Wilbur! Muy buenas cosas, lo suficiente para ponerme en marcha. Me gusta su idea de simplemente devolver una clase en el diccionario, hace que sea fácil hacer la introspección. – RickiG

10

Considere usar RestKit: http://restkit.org

Este marco tiene todo lo que necesita: abstracciones de REST y JSON, mapeo de objetos, incluso soporte de Core Data y muchas cosas realmente útiles, todo implementado de una manera personalizable y elegante.

ACTUALIZACIÓN: Ok, mientras escribía otro método de mapeo, decidí que ya no podía hacerlo y hice un pequeño marco. Introspecta las propiedades del objeto, y con un poco de ajuste le da una bonita descripción gratuita, isEqual/hashCode, soporte NSCoding gratuito, y permite generar a/desde mapeadores JSON (vale, en realidad, NSDictionary, pero quién lo usaría para otra cosa). Todas las comprobaciones de NSNull, los campos faltantes en JSON, los nuevos campos inesperados en JSON se manejan correctamente y se informan correctamente.

Si alguien quiere compartir esto con el público, podría darme algunos votos favorables o comentarios. Eventualmente lo haría, pero podría considerar compartirlo más rápido.

+0

Hace medio año que lo uso :) ¡sí, es muy recomendable! – RickiG

+0

Me pareció difícil de instalar. Y después de que ASIHTTPRequests dejó de ser compatible, soy más reacio a confiar en un marco más amplio. – zekel

0

¡Pruebe CSMapper, es increíble! Simplemente crea plists con el mismo nombre que la clase, mapea algunas propiedades, luego puedes asignar un diccionario a tus objetos con facilidad con una sola línea de código. Lo probé en muchos proyectos y lo encontré muy limpio y funcional. Me dio flexibilidad para responder a los cambios de API durante el ciclo de vida de desarrollo con facilidad.

https://github.com/marcammann/CSMapper

Actualmente estoy actualizando la documentación y la adición de al proyecto en un tenedor personal que esperemos que se fusionó pronto :)

https://github.com/AntonTheDev/CSMapper

0

Mi recomendación es utilizar la categoría Motis en NSObject. Es liviano, realiza validaciones automáticas para sus tipos de objetos y es muy fácil de usar.

Hay un exapmle en esta otra pregunta: Mapping JSON objects in custom objects

Esperamos que sea de utilidad.

1

Probé el mismo enfoque también. Mi problema principal era describir los tipos con precisión. Por ejemplo, para las matrices anidadas he usado algo como esto:

@{ @"friends" : @[[Person class]] } 

Pero las cosas se pusieron desordenado, porque necesitaba inventar más y más especial sintaxis de diferentes tipos, que quería apoyar. Al final, dejé de seguir este enfoque y busqué otra solución, también porque las comprobaciones de tiempo de ejecución que tenía que hacer disminuían la velocidad de la transformación.

Hay muchas soluciones disponibles en este momento. Sugeriría echar un vistazo al sitio web Awesome iOS. Y también me gustaría señalar JSON Class Generator, ya que le permite a

  • describir los tipos de precisión (se incluyen creadores de mapas especiales para fecha, etc.),
  • los tipos se comprueban de forma automática (desajustes reportados y los valores de retorno proporcionan),
  • que no tienen que pasar tiempo para escribir código repetitivo (y el código generado parece siempre "sólo trabajo")

odio para hacer publicidad, pero esta herramienta me ahorró un montón de trabajo, que he invertido en otras características de mi aplicación s. Salió un poco tarde, ahora que el mundo parece cambiar a Swift, pero aún más razón para no perder el tiempo escribiendo modelos en Objective-C.

Éstos son algunos ejemplos de código de conversión a/forma JSON:

// converting MyClass <-> NSDictionary 
MyClass  *myClass = [MyClass myClassWithDict:someJsonDictionary]; 
NSDictionary *jsonDict = [myClass toDict]; 

// converting NSDictionary <-> NSData 
NSDictionary *someJsonDictionary = [NSDictionary api_dictionaryWithJson:someJsonData]; 
NSData *jsonData = [someJsonDictionary api_toJson]; 

Las características mencionadas en un comentario anterior también son apoyadas por JSON Class Generator:

-isEqual, -hash, -description, -copy, NSCoding/NSSecureCoding/NSKeyedArchiver 

apoyo gratuito y comprobar si faltan/adicionales/NSNull/campos opcionales y tipos incorrectos en la respuesta JSON, informa errores, falla con gracia con los valores de retorno y lo hace mediante el envío de métodos en lugar de la introspección del tipo de tiempo de ejecución (que es más rápido).

Cuestiones relacionadas