【问题标题】:Deserialise nested/layered data without strong class hierarchies反序列化没有字符串类层次结构的嵌套/分层数据
【发布时间】:2021-09-17 05:39:32
【问题描述】:

我有一个设计/架构问题。我目前正在尝试优雅地将数据解析为单个数据类。但是数据是由各种协议包装的:A(B(C))。主要思想是能够在不同的层切换这些(如A(D(E))B(C))。但最后我只想返回一个包含所有内容的对象。

我目前的方法是使用继承来抽象每个类的层/协议。但是使用这种方法,我在切换层时不灵活,而不必重新编码类。请参阅下面的简化示例和示例数据。

所以我的问题是:是否有一种模式可以正确实现分层数据的这种反序列化,同时保持更改层的灵活性?

{                                               // Layer A
  "timestamp": "2021-07-06T10:01:00.000Z",
  "value": {                                    // Layer B
      "timestamp": "2021-07-06T10:00:30.000Z",
      "src": "any",
      "data": "some complex data string"        // Layer C
  }
}
class _Base { public JsonNode parse(JsonNode data) { return data; } }
class A extends _Base {
  public Instant aTimestamp;
  public JsonNode parse(JsonNode data) {
    JsonNode remaining = super.parse(data);
    aTimestamp = Instant.parse(remaining.get("timestamp").asText());
    return remaining.get("value");
  }
}
class B extends A {
  public Instant bTimestamp;
  public String src;
  public JsonNode parse(JsonNode data) {
    JsonNode remaining = super.parse(data);
    bTimestamp = Instant.parse(remaining.get("timestamp").asText());
    src = remaining.get("src").asText();
    return remaining.get("data");
  }
}
class C extends B {
  public String[] data;
  public JsonNode parse(JsonNode data) {
    JsonNode remaining = super.parse(data);
    this.data = remaining.asText().split(" ").
    return null;
  }
}

【问题讨论】:

    标签: java design-patterns architecture deserialization


    【解决方案1】:

    如果我在 C# 中执行此操作,我会尝试使用接口 - 因此,如果您想将 B 换成 D,那么只要类实现可能工作的接口。继承很好,但不如接口灵活。继承“是”事物,接口“具有”事物。见:HAS-A, IS-A terminology in object oriented language

    例如(伪代码):

    interface ILevel1{
      Instant bTimestamp;
      String src;
      ILevel2 iLevel2;
    }
    
    interface ILevel2{
      String[] data;
    }
    
    // your new pseudo code:
    
    class B : ILevel1 {
      public Instant bTimestamp;
      public String src;
      public ILevel2 parse(JsonNode data) {
        JsonNode remaining = super.parse(data);
        bTimestamp = Instant.parse(remaining.get("timestamp").asText());
        src = remaining.get("src").asText();
        return remaining.get("data");
      }
    }
    
    class C : ILevel2 {
      public String[] data;
      public JsonNode parse(JsonNode data) {
        JsonNode remaining = super.parse(data);
        this.data = remaining.asText().split(" ").
        return null;
      }
    }
    
    class D : ILevel1 {
      public Instant bTimestamp;
      public String src;
      public ILevel2 parse(JsonNode data) {
        JsonNode remaining = super.parse(data);
        bTimestamp = Instant.parse(remaining.get("timestamp").asText());
        src = "SRC is totally overrated";
        return remaining.get("data");
      }
    }
    

    这有意义吗?我的伪代码可能不正确,但关键是 B 类(和 D - 实现 ILevel1 的任何东西)将返回实现 ILevel2 的东西。

    我通常使用 C# 工作,虽然我做过 JSON 反序列化,但我不是专家。

    根据评论更新

    我认为我在伪代码中犯了一个错误 - 已更正。

    在您的示例中,作为接口的 super.parse(data) 将是什么 没有提供实现?

    更正接口没有,但类确实......所以虽然接口充当占位符,但在运行时会加载实际的实现,因此它仍然可以进行一些实际的解析。有意义吗?

    您如何将这些实现组合成一个类/对象 最后?

    您需要一些逻辑,这些逻辑基于一些输入,知道要实例化给定接口的哪个实现。有很多方法可以解决这个问题:可能位于某个公共库中的工厂,或类中的代码。

    假设可以在某个地方将控制在运行时加载哪个实现的规则编码到您的解决方案中 - 例如嵌入在实现本身或由位于它们外部的公共库驱动的数据中。

    这是每个的伪示例:

    // Decide in the factory:
    public ILevel1 Level1Factory(string someinput)
    {
      if(droid=='K2SO')
        return new B()
      else
        return new C()
    }
    
    // Decide in-line in the class - but still abstract out the factory:
    class X : ILevel1 {
      public Instant bTimestamp;
      public String src;
      public ILevel2 myLevel2;
    
      JsonNode parsedData = super.parse(data);
      if(droid=='K2SO')
        myLevel2 = Factory.MakeL2Type001(parsedData);
      else
        myLevel2 = Factory.MakeL2Type002(parsedData);
    
      ...
    }
    

    【讨论】:

    • 我明白了,也在研究接口,但解析不会接管。在您的示例中,super.parse(data) 会是什么,因为接口不提供实现?还有:你最终如何将这些实现组合成一个类/对象?虽然我的课程C 也包含AB 的所有定义,但在您的示例中,我需要为每个组合再次重新实现所有内容——或者我错过了什么?
    • super.parse(data) 是什么?老实说,我没有考虑太多,我只是在复制你的内容。我将更新我的答案以涵盖其他要点。
    • 啊,这解释了混乱。它是 C# 的 base 的 Java 等价物:它将调用定向到父/继承函数。
    猜你喜欢
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    相关资源
    最近更新 更多