【问题标题】:Parsing Graphite JSON Response with Gson使用 Gson 解析 Graphite JSON 响应
【发布时间】:2017-02-01 20:06:30
【问题描述】:

我正在编写一个 Java 库,用于与 Graphite 中的指标进行交互。 典型的 JSON 响应如下所示(取自官方文档):

[{
  "target": "entries",
  "datapoints": [
    [1.0, 1311836008],
    [2.0, 1311836009],
    [3.0, 1311836010],
    [5.0, 1311836011],
    [6.0, 1311836012]
  ]
}]

“datapoints”数组的第一个元素是值,第二个元素是时间戳。我已经建模了一个 GraphiteDataset 类,如下所示

class GraphiteDataset {
    private String target;
    private List<GraphiteDatapoint> datapoints;

    ....
}

和 GraphiteDatapoint 类

class GraphiteDatapoint {
    private Long timestamp;
    private Double value;

    ...
}

现在我需要将响应(见上文)解析到 GraphiteDataset 使用 Gson 上课。不幸的是,“数据点”的元素不是命名对象(例如{timestamp: 1234, value: 1.0},而是一个二维数组,所以我不能直接将其反序列化为某个类。目前我的解决方案是有一个中间类

class GraphiteIntermediateDataset {
    private String target;
    private List<String> datapoints;
    ...
}

将数据点作为字符串,然后我将它们解析为适当的 GraphiteDatapoint 实例。我认为我无法解决自定义反序列化程序。你有什么建议或技巧可以让这更方便一点吗?

【问题讨论】:

    标签: java gson graphite


    【解决方案1】:

    JSON [1.2, 123456]DoubleLong 的数组,但它们都是 Number,所以试试这个:

    class GraphiteDataset {
        private String target;
        private List<List<Number>> datapoints;
    
        ....
    }
    

    然后在解析后将datapoints 转换为你的类型,类似于:

    List<GraphiteDatapoint> points = datapoints.stream().
        .map(nums -> new GraphiteDatapoint(nums.get(0).doubleValue(), nums.get(1).intValue()))
        .collect(Collectors.toList());
    

    假设构造函数如下:

    class GraphiteDatapoint {
        private Long timestamp;
        private Double value;
        public GraphiteDatapoint(Double value, Long timestamp) {
            this.value = value;
            this.timestamp = timestamp;
        }
        ...
    }
    

    【讨论】:

    • 是的,成功了,谢谢!我曾尝试编写自定义反序列化器,但此解决方案要好得多。我现在的解决方案是保留具有List&lt;List&lt;Number&gt;&gt; 而不是List&lt;GraphiteDatapoint&gt; 但本质上相同的中间类。
    【解决方案2】:

    最终的解决方案是引入一个中间类GraphiteIntermediateDataset,如下所示:

    class GraphiteIntermediateDataset {
        private String target;
        private List<List<Number>> datapoints;
    }
    

    反序列化代码如下所示

    List<GraphiteIntermediateDataset> intermediateDatasetList = GSON.fromJson(raw, new TypeToken<List<GraphiteIntermediateDataset>>(){}.getType());
    
    GraphiteIntermediateDataset intermediateDataset = intermediateDatasetList.get(0);
    
    ... check if empty (which can happen), when true return an empty GraphiteDataset
    
    List<GraphiteDatapoint> gDatapoints = intermediateDataset
             .stream()
             .map(ds -> {
                return new GraphiteDatapoint(ds.get(0).longValue(),
                                             ds.get(1).doubleValue())
                }
             .collect(Collectors.toList());
    return new GraphiteDataset()
           .setDatapoints(gDatapoints);
    

    【讨论】:

      【解决方案3】:

      类型安全和正确的数据绑定是您的朋友。 Gson 有几种方法可以完成您需要的工作。比如声明数据传输对象:

      final class GraphiteDataset {
      
          final String target;
      
          // The incoming DTO has property `datapoints`, however Java conventions suggest dataPoints (as far as I understand English).
          @SerializedName("datapoints")
          final List<GraphiteDataPoint> dataPoints;
      
          // Actually, Gson does not need this constructor, and the DTO can even have a single private default one.
          // But in order to make it consistent with the next class just making it programmatically instantiable...
          // Also, but may be opinion-based, hiding constructors is really a good idea since one can hide the instantiation strategy whilst constructors cannot.
          private GraphiteDataset(final String target, final List<GraphiteDataPoint> dataPoints) {
              this.target = target;
              this.dataPoints = dataPoints;
          }
      
      }
      
      final class GraphiteDataPoint {
      
          final double value;
          final long timestamp;
      
          private GraphiteDataPoint(final double value, final long timestamp) {
              this.value = value;
              this.timestamp = timestamp;
          }
      
          // Instantiation must be accessible programmatically somehow
          static GraphiteDataPoint graphiteDataPoint(final double value, final long timestamp) {
              return new GraphiteDataPoint(value, timestamp);
          }
      
      }
      

      然后实现一个 GraphiteDataPoint JSON 反序列化器:

      // In Gson serializers and deserializers can only deal with intermediate Gson JSON tree representation of objects (JsonElement-s).
      // For some cases it's quite simple, if the given data to serialize/deserialize does not consume much memory
      final class GraphiteDataPointJsonDeserializer
              implements JsonDeserializer<GraphiteDataPoint> {
      
          private static final JsonDeserializer<GraphiteDataPoint> graphiteDataPointJsonDeserializer = new GraphiteDataPointJsonDeserializer();
      
          private GraphiteDataPointJsonDeserializer() {
          }
      
          // Not letting to instantiate a stateless (so it's thread-safe) deserializer twice or more
          static JsonDeserializer<GraphiteDataPoint> getGraphiteDataPointJsonDeserializer() {
              return graphiteDataPointJsonDeserializer;
          }
      
          @Override
          public GraphiteDataPoint deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
                  throws JsonParseException {
              final JsonArray asJsonArray = jsonElement.getAsJsonArray();
              final double value = asJsonArray.get(0).getAsJsonPrimitive().getAsDouble();
              final long timestamp = asJsonArray.get(1).getAsJsonPrimitive().getAsLong();
              return graphiteDataPoint(value, timestamp);
          }
      
      }
      

      或类型适配器:

      // Type adapters, unlike serializers and deserializers, are designed to work with streams.
      // They may look too low-level and tedious/hard to implement, but for some cases they can be useful in both serialization and deserialization.
      // For the case #1: no need to serialize nested objects recursively to transform them to JSON trees that can be important for large objects.
      // For the case #2: intermediate JSON trees are not necessary (but internal buffers are).
      final class GraphiteDataPointTypeAdapter
              extends TypeAdapter<GraphiteDataPoint> {
      
          private static final TypeAdapter<GraphiteDataPoint> graphiteDataPointTypeAdapter = new GraphiteDataPointTypeAdapter();
      
          private GraphiteDataPointTypeAdapter() {
          }
      
          static TypeAdapter<GraphiteDataPoint> getGraphiteDataPointTypeAdapter() {
              return graphiteDataPointTypeAdapter;
          }
      
          @Override
          public void write(final JsonWriter out, final GraphiteDataPoint value) {
              throw new UnsupportedOperationException("not implemented");
          }
      
          @Override
          public GraphiteDataPoint read(final JsonReader in)
                  throws IOException {
              in.beginArray();
              final double value = in.nextDouble();
              final long timestamp = in.nextLong();
              in.endArray();
              return graphiteDataPoint(value, timestamp);
          }
      
      }
      

      两种实现方式基本相同,但可能对您依赖数据(反)序列化策略和成本至关重要。使用示例:

      private static final String JSON = "[{\"target\":\"entries\",\"datapoints\":[[1.0,1311836008],[2.0,1311836009],[3.0,1311836010],[5.0,1311836011],[6.0,1311836012]]}]";
      
      // Gson is thread-safe and can be shared between threads, so no need to instantiate it every time it's needed
      private static final Gson gsonWithDeserializers = new GsonBuilder()
              .registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointJsonDeserializer())
              .create();
      
      private static final Gson gsonWithTypeAdapters = new GsonBuilder()
              .registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointTypeAdapter())
              .create();
      
      private static final TypeToken<List<GraphiteDataset>> graphiteDatasetsTypeToken = new TypeToken<List<GraphiteDataset>>() {
      };
      
      public static void main(final String... args) {
          dumpGraphiteDatasets(gsonWithDeserializers.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
          dumpGraphiteDatasets(gsonWithTypeAdapters.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
      }
      
      private static void dumpGraphiteDatasets(final Iterable<GraphiteDataset> graphiteDatasets) {
          graphiteDatasets.forEach(graphiteDataset -> {
              out.println(graphiteDataset.target);
              graphiteDataset.dataPoints.forEach(graphiteDataPoint -> {
                  out.print("  ");
                  out.print(graphiteDataPoint.value);
                  out.print(" ");
                  out.println(graphiteDataPoint.timestamp);
              });
          });
      }
      

      输出:

      entries
        1.0 1311836008
        2.0 1311836009
        3.0 1311836010
        5.0 1311836011
        6.0 1311836012
      entries
        1.0 1311836008
        2.0 1311836009
        3.0 1311836010
        5.0 1311836011
        6.0 1311836012
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-19
        相关资源
        最近更新 更多