首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用反射递归序列化对象?

如何使用反射递归序列化对象?
EN

Stack Overflow用户
提问于 2010-04-12 23:12:58
回答 3查看 10.4K关注 0票数 5

我想导航到对象的第N级,并以字符串格式序列化它的属性。例如:

代码语言:javascript
复制
class Animal {
   public String name;
   public int weight;
   public Animal friend;
   public Set<Animal> children = new HashSet<Animal>() ;
}

应该像这样序列化:

代码语言:javascript
复制
{name:"Monkey",
 weight:200,
 friend:{name:"Monkey Friend",weight:300 ,children:{...if has children}},
 children:{name:"MonkeyChild1",weight:100,children:{... recursively nested}}
}

您可能会注意到,它类似于将对象序列化为json。我知道有很多歌词(Gson,Jackson…)可以做到这一点,你能给我一些关于如何自己写这篇文章的指导性意见吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-04-12 23:24:28

序列化基本上就是深度克隆。

您需要跟踪每个对象引用的重现情况(例如,通过使用IdentityHashMap)。无论你的最终实现方法是什么(如果不是外部库),记得检查对象的重现,否则你可能会陷入无限循环(当对象A引用了对象B,而对象B又引用了对象A或对象图中更复杂的循环)。

一种方法是使用类似DFS的算法遍历对象图,并从那里构建克隆(序列化字符串)。

这段伪代码有望解释如何:

代码语言:javascript
复制
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的地方。

在查找反射字段时(这是node.expand()部分),请记住也要遍历超类字段。

反射不应该用在“正常”的开发案例中。反射将代码作为数据处理,您可以忽略所有正常的对象访问限制。我只在测试中使用了这个反射深度拷贝的东西:

  1. 在一项检查不同类型对象的深度对象图相等性的测试中
  2. 在一项分析对象图大小和其他属性的测试中
票数 5
EN

Stack Overflow用户

发布于 2010-04-12 23:27:07

Google Gson可以在一行中完成此特定任务:

代码语言:javascript
复制
String json = new Gson().toJson(animal);

顺便说一下,另一种方法也很简单:

代码语言:javascript
复制
Animal animal = new Gson().fromJson(json, Animal.class);

我还没有看到另一个JSON序列化程序能够更好地支持泛型、集合/映射和(嵌套的) javabeans。

更新:说到这一点,你只需要了解反射。我建议您先完成Sun tutorial on the subject。简而言之,您可以使用Object#getClass()以及java.lang.Classjava.lang.reflect.Method等提供的所有方法来确定其中的哪一个。Google Gson是开源的,你也可以从中受益。

票数 7
EN

Stack Overflow用户

发布于 2010-04-12 23:32:24

一种简单的方法是使用访问者模式来,使您的编码实现与业务对象分离。有些人会争辩说,您可以简单地将Externalizable接口与readExternal / writeExternal一起实现,但这有以下问题:

  • 您的协议被嵌入到您的业务对象中,这意味着它分布在您的代码库中,而不是分布在一个地方。
  • 您的应用程序不能支持多个协议(由Google protocol Buffers提供)。

示例

代码语言:javascript
复制
/**
 * 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实现,当OutputStreamvisitXXX方法被调用时,它将把每个对象“推”到一个each中;

代码语言:javascript
复制
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();
  }
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2623091

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档