Parsing marco que se ocupa bien de referencias circulares en JSON

Estoy trabajando en un proyecto de Android y actualmente estoy tratando de averiguar cómo deserializar algunos JSON de nuestra API s que incluye ciclos de referencia en un gráfico de objetos, que luego puedo manipular y almacenar en una base de datos. Déjame dar un ejemplo:

 { "id": "24", "name": "Bob", "friends": [ { "id": "13", "name": "Alice", "friends": [ { "id": "24" // and we have a circular reference } ] } ] } 

Aquí, una persona objeto llamado Bob es amigo de la persona Alice , y Alice es a su vez amigos con Bob . Dado que la relación es recursiva, la relación de amigos de Alice con Bob no se realiza como un objeto de persona completa, pero solo se proporciona su id .

¿Qué herramientas utiliza para realizar los pasos mencionados anteriormente? Intenté implementar la parte de mapeo de objetos con Jackson pero no encontré una solución para el requisito de ciclo. He encontrado una discusión en curso sobre este tema que menciona JSOG que puede ser útil, pero nuestros APIs son fijos y no son compatibles con JSOG.

Básicamente lo que estoy buscando es algo como RestKit (iOS framework) para Android.

3 Solutions collect form web for “Parsing marco que se ocupa bien de referencias circulares en JSON”

Una vez que la API se haya corregido, la implementaría de esta manera:

Desde la perspectiva de DB, tendría 2 tablas – UserTable y RelationsTable para mantener todos los bordes de su gráfico amigos:
Introduzca aquí la descripción de la imagen

Es decir, la idea es mantener a los usuarios en una tabla y sus relaciones en la tabla de relaciones . También permite agregar algo de lógica adicional encima de él más tarde (por ejemplo, el usuario oculta su conexión o bloquea a alguien, etc. – cualquier posible borde del gráfico). Además, permite mitigar problemas con referencias circulares .

Como un marco para recuperar datos de servicio y parse jsons, usaría Retrofit .

En primer lugar, definiría las clases UserBase y User :

 public class UserBase { public string id; } public final class User extends UserBase { public string name; public List<UserBase> friends; // user's "real" friends, not just ids, fills from SQLite public List<User> userFriends; } 

Donde, como puedes ver, friends es una lista de objetos UserBase para Retrofit para analizar el objeto de JSON y userFriends – la lista, que userFriends de SQLite manualmente en pasos adicionales.

Ahora, vamos a definir algunas clases de ayuda para operar con DBs:

 public interface Dao<TItem> { void add(List<TItem> items); void removeAll(); List<TItem> getAll(); } .... public abstract class AbstractDao<TItem> implements Dao<TItem> { protected final SQLiteDatabase database; protected final SqlUtilities sqlUtilities; public AbstractDao(SQLiteDatabase database, SqlUtilities sqlUtilities) { this.database = database; this.sqlUtilities = sqlUtilities; } } 

Ahora necesitamos Dao's para RelatedTable y UserTable:

 public class UserRelation { public String mainUserId; public String relatedUserId; } ... public interface UserRelationDao extends Dao<UserRelation> { ... List<User> getFriends(String userId); ... } ... public interface UserDao extends Dao<User> { ... void addWithIgnore(List<TItem> items); void update(List<TItem> items); void upsert(List<TItem> items); User getById(String userId); ... } 

Una vez hecho esto, podemos implementar realmente estas interfaces:

DefaultUserRelationDao clase:

 public class DefaultUserRelationDao extends AbstractDao<UserRelation> implements UserRelationDao { static final String MAIN_USER_COLUMN = "mainuser"; static final String RELATED_USER_COLUMN = "relateduser"; private static final String[] COLUMN_NAMES = new String[]{ MAIN_USER_COLUMN, RELATED_USER_COLUMN, }; private static final String[] COLUMN_TYPES = new String[]{ "TEXT", "TEXT", }; private static final String TABLE = "userrelation"; static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES); static final String ALL_CONNECTED_USERS = "SELECT " + Joiner.on(",").join(DefaultUserDao.COLUMN_NAMES) + " FROM " + UserTable.TABLE_NAME + "," + TABLE + " WHERE " + RELATED_USER_COLUMN + "=" + DefaultUserDao.USER_ID_COLUMN; public DefaultUserRelationDao(SQLiteDatabase database, SqlUtilities sqlUtilities) { super(database, sqlUtilities); } @Override public void add(List<UserRelation> userRelations) { try { database.beginTransaction(); ContentValues contentValues = new ContentValues(); for (UserRelation relation : userRelations) { sqlUtilities.setValuesForUsersRelation(contentValues, relation); database.insertOrThrow(TABLE, null, contentValues); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } } @Override public List<User> getFriends(String userId) { Cursor cursor = database.rawQuery(ALL_CONNECTED_USERS, new String[]{userId}); return sqlUtilities.getConnectedUsers(cursor); } } 

Y DefaultUserDao clase:

 public final class DefaultUserDao extends AbstractUDao<User> implements UserDao { public static final String USER_ID_COLUMN = "userid"; static final String USER_NAME_COLUMN = "username"; public static final String[] COLUMN_NAMES = new String[]{ USER_ID_COLUMN, USER_NAME_COLUMN, }; private static final String TABLE = "users"; private static final String SELECT_BY_ID = SqlUtilities.getSelectWhereStatement(TABLE, COLUMN_NAMES, new String[]{ USER_ID_COLUMN }); static final String CREATE_TABLE = SqlUtilities.getCreateStatement(TABLE, COLUMN_NAMES, COLUMN_TYPES); public DefaultUserDao(SQLiteDatabase database, SqlUtilities sqlUtilities) { super(database, sqlUtilities); } @Override public void add(List<User> users) { try { database.beginTransaction(); ContentValues contentValues = new ContentValues(); for (User user : users) { sqlUtilities.setValuesForUser(contentValues, user); database.insertOrThrow(UserTable.TABLE_NAME, null, contentValues); } database.setTransactionSuccessful(); } finally { database.endTransaction(); } } @Override public User getById(String userId) { return getUserBySingleColumn(SELECT_BY_ID, userId); } ..... private User getUserBySingleColumn(String selectStatement, String value) { Cursor cursor = database.rawQuery(selectStatement, new String[]{value}); List<User> users = sqlUtilities.getUsers(cursor); return (users.size() != 0) ? users.get(0) : null; } } 

Para crear nuestras tablas, necesitamos extender SQLiteOpenHelper y en onCreate() realidad crear tablas:

 public final class DatabaseHelper extends SQLiteOpenHelper { static final String DATABASE_NAME = "mysuper.db"; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, SCHEMA_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DefaultUserDao.CREATE_TABLE); db.execSQL(DefaultUserRelationDao.CREATE_TABLE); } ... } 

Ahora, sugeriría para definir la interfaz de LocalStorage con todas las acciones posibles con caché:

  • Obtener todos los usuarios
  • Obtener usuario por id
  • Agregar usuarios
  • Agregar conexión entre usuarios
  • Etc.

     public interface LocalStorage { User getUserById(String userId); void addUsers(List<User> users); .... } 

Y su implementación:

 public final class SqlLocalStorage implements LocalStorage { private UserDao userDao; private UserRelationDao userRelationDao; private SQLiteDatabase database; private final Object initializeLock = new Object(); private volatile boolean isInitialized = false; private SqlUtilities sqlUtilities; // there database is // SQLiteOpenHelper helper = new DatabaseHelper(context); // database = helper.getWritableDatabase(); public SqlLocalStorage(SQLiteDatabase database, SqlUtilities sqlUtilities) { this.database = database; this.sqlUtilities = sqlUtilities; } @Override public User getUserById(String userId) { initialize(); User user = userDao.getById(userId); if (user == null) { return null; } List<User> relatedUsers = userRelationDao.getFriends(userId); user.userFriends = relaterUsers; return user; } @Override public void addUsers(List<User> users) { initialize(); for (User user : users) { for (UserBase friend : user) { UserRelation userRelation = new UserRelation(); userRelation.mainUserId = user.id; userRelation.relatedUserId = friend.id; UserRelation userRelationMutual = new UserRelation(); userRelationMutual.mainUserId = friend.id; userRelationMutual.relatedUserId = user.id; userRelationDao.add(userRelation); userRelationMutual.add(userRelation) } } userDao.addWithIgnore(users); } void initialize() { if (isInitialized) { return; } synchronized (initializeLock) { if (isInitialized) { return; } Log.d(LOG_TAG, "Opens database"); userDao = new DefaultUserDao(database, sqlUtilities); userRelationDao = new DefaultUserRelationDao(database, sqlUtilities); isInitialized = true; } } } 

Último paso – el uso real de la misma:

 //somewhere in non-UI thread List<User> users = dataSource.getUsers(); localStorage.addUsers(users); final User userBob = localStorage.getUserById("42"); 

¡NÓTESE BIEN! Estoy usando mucho aquí mi clase personalizada SqlUtilities. Por desgracia, es demasiado grande para publicarlo aquí, pero sólo un ejemplo para dar algunas ideas de lo que está dentro – aquí es cómo getUsers (Cursor cursor) se ve allí:

 ..... public List<User> getUsers(Cursor cursor) { ArrayList<User> users = new ArrayList<>(); try { while (cursor.moveToNext()) { users.add(getUser(cursor)); } } finally { cursor.close(); } return users; } private User getUser(Cursor cursor) { User user = new User(cursor.getString(0)); user.FullName = cursor.getString(1); .... return user; } ..... 

Espero que me perdone saltando algunos detalles (especialmente, en cuanto al caso, cuando DB tiene que ser actualizado, cuando los datos no están llenos y además de conseguirlo desde la caché, usted tiene que recuperarlo del servidor primero, y luego cargarlo en La caché, etc). Si alguna parte crucial falta – por favor, publicarlo en los comentarios y estaré encantado de actualizar la publicación.

Espero que te ayude.

Puedes echar un vistazo a JSON-RPC . Este es un buen marco que soporta el análisis de JSON y el mapeo de objetos de la relación de objetos complejos.

Yo diría que usted está tratando de resolver el problema equivocado y el verdadero problema es que su representación de datos está roto. Así como el problema de las referencias circulares es también ineficaz en que cada amigo se duplica para cada amistad. Es mejor aplastar su lista de personas como esta:

 [ { "id": "13", "name": "Alice", "friends": ["24"] }, { "id": "24", "name": "Bob", "friends": ["13"] } ] 

Almacene la lista en un HashMap<Integer, Person> (o SparseArray<Person> ). ¡Trabajo hecho!

  • ¿Cómo obtener datos Instagram de un usuario en Android?
  • Publicar json de Android en la página PHP
  • Cómo almacenar JSON en SQLite
  • Análisis de JSON con Retrofit
  • @IntDef Anulación de soporte de Android con deserialización de Jackson
  • ¿Cómo analizar después de JSON usando GSON en android?
  • Almacenamiento de datos de Android, Cuándo utilizar SqlLite y cuándo usar JSON, alternativas de Linq
  • Java.lang.ClassCastException: org.json.simple.JSONArray no se puede convertir en org.json.JSONArray
  • Enviar JSON como una solicitud POST al servidor por AsyncHttpClient
  • Asignación de un JSONObject a una lista con Flexjson en Java / Android
  • Mapeo de Json a POJO en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.