2010-04-12 2 views
4

Я хочу перейти на N-й уровень объекта и сериализовать его свойства в формате String. Для примера:Как рекурсивно сериализовать объект с помощью отражения?

class Animal { 
    public String name; 
    public int weight; 
    public Animal friend; 
    public Set<Animal> children = new HashSet<Animal>() ; 
} 

должен сериализовать так:

{name:"Monkey", 
weight:200, 
friend:{name:"Monkey Friend",weight:300 ,children:{...if has children}}, 
children:{name:"MonkeyChild1",weight:100,children:{... recursively nested}} 
} 

И вы можете, вероятно, заметили, что он похож на сериализацию объекта в JSON. Я знаю, что есть много libs (Gson, Jackson ...), можете ли вы дать мне некоторые поучительные идеи о том, как написать это самостоятельно?

+0

Есть ли определенная часть этого, что трудно? – Armand

+1

Если вы знаете, что есть много библиотек, почему бы вам просто не использовать их? –

+0

Можете ли вы сообщить нам, что вы пробовали до сих пор, поэтому у нас есть отправная точка? –

ответ

4

Сериализация в основном глубокое клонирование.

Вам необходимо отслеживать каждую ссылку на объекты для повторных обменов (например, используя IdentityHashMap). Какой бы то ни было ваш окончательный метод реализации (если не внешняя библиотека), не забудьте проверить на наличие повторных вызовов или вы можете оказаться в бесконечном цикле (когда объект A ссылается на объект B, который снова ссылается на объект A или что-то более сложное в графа объектов).

Один из способов - пройти через графа объектов с помощью DFS-подобного алгоритма и построить клон (сериализованную строку) оттуда.

Этот псевдо-код, мы надеемся, объясняет, как:

visited = {} 
function visit(node) { 
    if node in visited { 
    doStuffOnReoccurence(node) 
    return 
    } 
    visited.add(node) 
    doStuffBeforeOthers(node) 
    for each otherNode in node.expand() visit(otherNode) 
    doStuffAfterOthers(node) 
} 

Посещаемая установлено в примере, где я хотел бы использовать набор личных данных (если есть один) или IdentityHashMap.

При поиске полей отражательно (thats node.expand() часть) помните, чтобы пройти через поля суперкласса также.

Отражение не должно использоваться в «нормальном» случае развития. Reflection обрабатывает код как данные, и вы можете игнорировать все обычные ограничения доступа к объекту. Я использовал этот отражающий глубокий материал копирования только для испытаний:

  1. В тесте, что проверили различные виды объектов для глубокого объекта графа равенства

  2. В тесте, что анализируемого размер объекта графа и другие свойства

+0

Да, вы упомянули об IdentityHashMap, я заметил, что автор также использовал IdentityHashMap в своих примерах кодов. Но я не понимаю IdentityHashMap, даже я читал java-документацию об этом. – Sawyer

+2

Разница по сравнению с обычной картой заключается в том, что она использует идентификатор (==) как ключ вместо равных. Когда вы делаете глубокий клон, вам нужно знать, когда что-то действительно является одним и тем же объектом. – mkorpela

5

Google Gson может сделать эту конкретную задачу в одной строке:

String json = new Gson().toJson(animal); 

Другой путь круглый, кстати, также, как легко:

Animal animal = new Gson().fromJson(json, Animal.class); 

Я не видел еще сериалайзер JSON еще с лучшей поддержкой для дженериков, коллекций/карты и (вложенными) JavaBeans.

Обновление: К тому же вам просто нужно узнать об API отражения. Я рекомендую сначала пройти через Sun tutorial on the subject. В двух словах вы можете использовать Object#getClass() и все методы, предоставленные java.lang.Class, java.lang.reflect.Method и т. Д., Чтобы определить тот и другой. Google Gson - это с открытым исходным кодом, а также ваша польза от этого.

+0

Спасибо, я много использую Gson и jackson, jsonlib. Я хочу знать, как написать это, используя отражение. – Sawyer

+0

А? Почему вы хотите изобрести колесо? В любом случае: Gson является открытым исходным кодом. Примите ваше преимущество. – BalusC

+0

lol, отец json сказал, хорошая вещь, чтобы изобретать колеса, это то, что вы можете найти круглый. да, использование Gson просто потрясающе, но я до сих пор не знаю, как написать это сам, что если в какой-то день я столкнулся с подобными проблемами, но нет возможности использовать libs? – Sawyer

4

Чистый способ решения этой проблемы является использование посетителя шаблон для сохранить вашу реализацию кодирования отдельно от бизнес-объектов. Некоторые люди утверждают, что вы можете просто реализовать интерфейс Externalizable вместе с readExternal/writeExternal но есть проблемы, которые:

  • Ваш протокол embededded в бизнес-объекта, то есть она распространяется по всему вашему кодовую а не в одно место.
  • Приложение не может поддерживать несколько протоколов (как предложено буферами протокола Google).

Пример

/** 
* Our visitor definition. Includes a visit method for each 
* object it is capable of encoding. 
*/ 
public interface Encoder { 
    void visitAnimal(Animal a); 
    void visitVegetable(Vegetable v); 
    void visitMineral(Mineral m); 
} 

/** 
* Interface to be implemented by each class that can be encoded. 
*/ 
public interface Encodable { 
    void applyEncoder(Encoder e); 
} 

public class Animal implements Encodable { 
    public void applyEncoder(Encoder e) { 
    // Make call back to encoder to encode this particular Animal. 
    // Different encoder implementations can be passed to an Animal 
    // *without* it caring. 
    e.visitAnimal(this); 
    } 
} 

Обычно можно было бы затем определить динамическую Encoder реализацию, которая будет «толкать» каждый объект к OutputStream, когда его метод visitXXX называется; например

public class EncoderImpl implements Encoder { 
    private final DataOutputStream daos; 

    public EncoderImpl(File file) throws IOException { 
    this.daos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); 
    } 

    public void visitAnimal(Animal a) {   
    daos.writeInt(a.getWeight()); 
    daos.writeUTF(a.getName()); 

    // Write the number of children followed by an encoding of each child animal. 
    // This allows for easy decoding. 
    daos.writeInt(a.getChildren().size()); 

    for (Animal child : a.getChildren()) { 
     visitAnimal(child); 
    } 
    } 

    // TODO: Implement other visitXXX methods. 

    /** 
    * Called after visiting each object that requires serializing. 
    */ 
    public void done() { 
    daos.close(); 
    } 
} 
Смежные вопросы