【问题标题】:Are there good alternatives for serializing enums in Java?在 Java 中序列化枚举有很好的选择吗?
【发布时间】:2016-08-01 13:34:15
【问题描述】:

Java 语言从添加枚举中受益匪浅;但不幸的是,它们在具有不同代码级别的系统之间发送序列化对象时效果不佳。

示例:假设您有两个系统 A 和 B。它们都以相同的代码级别开始,但在某个时间点开始在不同的时间点看到代码更新。现在假设有一些

public enum Whatever { FIRST; }

还有其他对象保留对该枚举常量的引用。这些对象被序列化并从 A 发送到 B,反之亦然。现在考虑 B 有一个更新版本的whatever

public enum Whatever { FIRST; SECOND }

然后:

class SomethingElse implements Serializable { ...
  private final Whatever theWhatever;
  SomethingElse(Whatever theWhatever) {
    this.theWhatever = theWhatever; ..

被实例化...

SomethingElse somethin = new SomethingElse(Whatever.SECOND)

然后序列化并发送到 A(例如作为某些 RMI 调用的结果)。这很糟糕,因为现在在 A 的反序列化过程中会出现错误:A 知道 What enum 类,但在没有 SECOND 的版本中。

我们很难想到这一点;现在我非常渴望将枚举用于实际上“非常适合枚举”的情况;仅仅是因为我知道以后我不能轻易地扩展现有的枚举。

现在我想知道:是否有(好的)策略来避免此类与枚举的兼容性问题?或者我真的必须回到“预枚举”时代吗?并且不使用枚举,但必须依赖我在所有地方都使用纯字符串的解决方案?

更新:请注意,使用 serialversionuid 在这里根本没有帮助。那件事只会帮助您做出“更明显”的不兼容更改。但重点是:我不在乎为什么反序列化会失败——因为我必须避免它发生。而且我也无法改变我们序列化对象的方式。我们正在做 RMI;我们正在序列化为二进制;我没有办法改变它。

【问题讨论】:

  • 这不是任何课程的问题吗?如果您在较新版本中更改字段,则旧版本的反序列化将失败。
  • 不一定。添加字段是完全有效的操作。如果你使用纯字符串,你显然会松散编译时间检查;但是如果对象字段是字符串,那么该字段可以携带 any 值而不会导致此类问题。
  • 你可以使用常量——它们可以很好地序列化,并且它们有许多类似枚举的属性
  • 我同意@DanielM。对两个系统使用常量或使用相同的 Whatever 枚举定义。
  • 这更多是 Java 序列化的问题,而不是枚举的问题。序列化与代码紧密耦合,这使得它不适合系统之间甚至同一系统的不同版本之间的长期存储和互操作性。使用 JSON 或 XML 等标准格式而不是 Java 序列化以实现更松散的耦合。

标签: java serialization enums


【解决方案1】:

正如 @Jesper 在 cmets 中提到的,我会推荐 JSON 之类的东西来进行服务间通信。这将使您能够更好地控制未知 Enum 值的处理方式。

例如,使用总是很棒的Jackson,您可以使用Deserialization Features READ_UNKNOWN_ENUM_VALUES_AS_NULLREAD_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE。两者都将允许您的应用程序逻辑处理您认为合适的未知枚举值。

示例(直接来自 Jackson 文档)

enum MyEnum { A, B, @JsonEnumDefaultValue UNKNOWN }
...
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);

MyEnum value = mapper.readValue("\"foo\"", MyEnum.class);
assertSame(MyEnum.UNKNOWN, value);

【讨论】:

  • 当然;但至少目前,我们的架构是这样的;没有机会进行这种根本性的改变。我们不得不忍受序列化对象多年......
  • @GhostCat 我猜你可以使用custom Java Deserialization 来定义如何反序列化枚举以及如何处理未知值。
  • 附录:我认为自定义序列化不会起作用 - 枚举的处理方式不同(请参阅stackoverflow.com/questions/15521309/…)它说 枚举常量被序列化的过程不能定制那里。
  • 附录 2 --- 正如您从我自己写下的答案中看到的那样,您的评论启发了我……我想我找到了一个适合我的解决方案。
【解决方案2】:

在反复讨论不同的解决方案后,我根据@GuiSim 的建议想出了一个解决方案:可以构建一个包含枚举值的类。这个类可以

  1. 进行自定义反序列化;因此我可以防止在反序列化过程中不会出现异常
  2. 提供简单的方法,如isValid()getEnumValue():第一个告诉你枚举反序列化是否真的有效;第二个返回反序列化的枚举(或抛出异常)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-28
    • 2013-08-24
    • 1970-01-01
    • 2014-08-10
    • 2013-09-01
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多