【问题标题】:GSON: How to move fields to parent objectGSON:如何将字段移动到父对象
【发布时间】:2012-06-05 14:09:08
【问题描述】:

我正在使用 Google GSON 将我的 Java 对象转换为 JSON。

目前我的结构如下:

"Step": {
  "start_name": "Start",
  "end_name": "End",
  "data": {
    "duration": {
      "value": 292,
      "text": "4 min."
    },
    "distance": {
       "value": 1009.0,
       "text": "1 km"
    },
    "location": {
       "lat": 59.0000,
       "lng": 9.0000,
       "alt": 0.0
    }
  }
}

目前Duration 对象位于Data 对象内。我想跳过Data 对象并将Duration 对象移动到Step 对象,如下所示:

"Step": {
  "start_name": "Start",
  "end_name": "End",
  "duration": {
    "value": 292,
    "text": "4 min."
  },
  "distance": {
     "value": 1009.0,
     "text": "1 km"
  },
  "location": {
     "lat": 59.0000,
     "lng": 9.0000,
     "alt": 0.0
  }
}

如何使用 GSON 做到这一点?

编辑:我尝试使用 TypeAdapter 来修改 Step.class,但在 write-method 中我无法将我的持续时间对象添加到 JsonWriter。

【问题讨论】:

    标签: java json gson


    【解决方案1】:

    您可以通过编写,然后为Step 注册一个custom serializer 来做到这一点,并确保在其中使用Duration 等,而不是Data

    // registering your custom serializer:
    GsonBuilder builder = new GsonBuilder ();
    builder.registerTypeAdapter (Step.class, new StepSerializer ());
    Gson gson = builder.create ();
    // now use 'gson' to do all the work
    

    下面的自定义序列化程序的代码,我正在写下我的头顶。它错过了异常处理,并且可能无法编译,并且会减慢诸如重复创建 Gson 实例的速度。但它代表了您想要做的类型

    class StepSerializer implements JsonSerializer<Step>
    {
      public JsonElement serialize (Step src,
                                    Type typeOfSrc,
                                    JsonSerializationContext context)
        {
          Gson gson = new Gson ();
          /* Whenever Step is serialized,
          serialize the contained Data correctly.  */
          JsonObject step = new JsonObject ();
          step.add ("start_name", gson.toJsonTree (src.start_name);
          step.add ("end_name",   gson.toJsonTree (src.end_name);
    
          /* Notice how I'm digging 2 levels deep into 'data.' but adding
          JSON elements 1 level deep into 'step' itself.  */
          step.add ("duration",   gson.toJsonTree (src.data.duration);
          step.add ("distance",   gson.toJsonTree (src.data.distance);
          step.add ("location",   gson.toJsonTree (src.data.location);
    
          return step;
        }
    }
    

    【讨论】:

    • 我只需要serialize,所以deserialize不重要。以上只是我的结构的一个例子。事实上,我的数据对象包含额外的字段,这些字段需要包含在toJsonTree 中。任何线索我怎么能做到这一点?
    • 真的取决于。显示一个带有两个附加字段的示例,我会看看我能想到什么。警告:我的回答基于 Gson 文档。我以前从来没有做过这样奇怪的事情。
    • 我已经用数据对象内的其他字段更新了我的问题。
    • @DennisMadsen - 我已更新我的答案以反映您的更改。祝你好运。
    • 谢谢。有没有办法从 src 步骤创建 step 变量并删除它的数据对象?如果可能的话,我不必再次添加开始和结束名称。
    【解决方案2】:

    在这种情况下,我为嵌套的data 字段注册TypeAdapter。在适配器内data 的字段被添加到父对象。无需为封闭类创建适配器。

    public class Step {
        private String startName;
        private endName;
        @JsonAdapter(JsonFlatMapAdapter.class)
        private Map<String, Object> data;
        ...
    }
    
    public class JsonFlatMapAdapter extends TypeAdapter<Map<String, Object>> {
    
        @Override
        public void write(JsonWriter out, Map<String, Object> value) throws IOException {
            out.nullValue();
            Gson gson = new Gson();
            value.forEach((k,v) -> {
                try {
                    out.name(k).jsonValue(gson.toJson(v));
                } catch (IOException e) {
                }
            });
        }
    
        @Override
        public Map<String, Object> read(JsonReader in) throws IOException {
            return null;
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      我认为在 gson 中没有漂亮的方法可以做到这一点。也许从初始 json 中获取 java 对象(Map),删除数据,放入持续时间并序列化为 json:

      Map initial = gson.fromJson(initialJson);
      
      // Replace data with duration in this map
      Map converted = ...
      
      String convertedJson = gson.toJson(converted);
      

      【讨论】:

        【解决方案4】:

        遇到了同样的问题。 @ArjunShunkar 的回答为我指明了正确的方向。我修复了它编写自定义序列化程序,但略有不同:

        public class StepSerializer implements JsonSerializer<Step> {
            @Override
            public JsonElement serialize(Step src, Type typeOfSrc, JsonSerializationContext context) {
                JsonObject step = new JsonObject();
                step.add ("start_name", context.serialize(src.start_name);
                step.add ("end_name",   context.serialize(src.end_name);
        
                JsonObject data = context.serialize(src.data).getAsJsonObject();
                data.entrySet().forEach(entry -> {
                    step.add(entry.getKey(), entry.getValue());
                });
        
                return step;
            }
        }
        

        这可以进一步改进,“start_name”和“end_name”道具仍然是硬编码的。可以通过遍历根对象的 entrySet 并在其中排除“数据”来删除,只留下需要展开硬编码的元素。

        【讨论】:

          猜你喜欢
          • 2016-11-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-18
          • 1970-01-01
          • 2019-09-17
          • 1970-01-01
          相关资源
          最近更新 更多