【问题标题】:how to customize JSON output to finer details如何自定义 JSON 输出以获得更详细的信息
【发布时间】:2012-10-04 14:48:29
【问题描述】:

这是为所有 JSON 大脑准备的。

假设我有一个类似的界面:

public interface Model {
  String getName();
  String getDateCreated();
  String getTimeCreated();
  String getLastUpdated();
  String getLastUpdatedTime();
}

那么假设我有一个 Item 对象:

public class Item {

  private String name;
  private Date created;
  private Date updated;

  ..setters and getters
}

那么假设我有一个模型包装器:

public class ItemModel implements Model {

  private final Item item;

  public ItemModel(Item item){
      this.item = item;
  }

  public String getName(){
      return item.getName();
  }

  public String getDateCreated(){
      Date created = item.getCreated();
      //format the date nicely and return a date string
  }

  public String getTimeCreated(){
      Date created = item.getCreated();
      //format the date nicely and return a time string
  }

  ...you get the gyst, right?
}

问题是:

  1. 如何序列化 ItemModel 以便 json 输出反映模型接口上的 getter 名称?
  2. 具体来说,哪个库最容易做到这一点?您如何使用特定的库来实现这一点?
  3. 最后,我不想序列化包装在 ItemModel 中的 Item 对象

我希望输出是这样的:

{item : {name: '', dateCreated : '', timeCreated : '', lastUpdated : '', lastUpdatedTime : ''}

提前致谢!


我认为我应该分享到目前为止我所能提供的内容,并获得您的反馈。它几乎可以满足我的要求,但我不确定它是否正确:

我在这里被一位用户指向了genson,我觉得它很有趣。

首先我创建了一个序列化器

公共类 ModelSerializer 实现序列化器 {

@Override
public void serialize(T target, ObjectWriter writer, Context cntxt) throws TransformationException, IOException {
    writer.beginObject();
    for (Method method : target.getClass().getMethods()) {
        String methodName = method.getName();
        if (methodName.startsWith("get")) {
            int index = "get".length();
            String valueName = Character.toLowerCase(methodName.charAt(index)) + method.getName().substring(index + 1);
            try {
                String valueString = (String) method.invoke(target, new Object[]{});
                writer.writeName(valueName).writeValue(valueString);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            } catch (IllegalAccessException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            } catch (InvocationTargetException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            }
        }
    }
    writer.endObject();
}

}

接下来,我创建了 Genson 并通过了 Serializer

Genson genson = new Genson.Builder().withSerializer(new ModelSerializer(), Model.class).create();

现在,当我通过 ViewModel 时,我得到的字段格式符合我的预期

模型 viewModel = new ItemModel(domainModel);其中 domainModel 是 Item 的一个实例 System.out.println(genson.serialize(viewModel));

【问题讨论】:

  • 很高兴您喜欢 Genson!但是你为什么要做所有内省的事情呢?您期望的确切输出是什么?您应该将您的 Serializer 定义为 ModelSerializer implements Serializer { ... class code ... } 然后只需将其注册为 Builder().withConverters(new ModelSerializer()).create();
  • 你为什么不直接做 new Genson().serialize(viewModel); ?您将获得与自定义序列化程序相同的输出(我刚刚测试了两种解决方案)。
  • 您是否注意到 Model 接口只返回字符串值,而 Item 返回字符串和日期?您必须对日期输出进行一些格式化,这是导致问题的原因。
  • 是的,我注意到了,但是通过将@JsonIgnore 放在项目上,他将不会被序列化,并且只会使用来自 ItemModel 的 get 方法(使用您在这些方法中定义的格式)。这样做能解决你的问题吗?
  • 啊,您可以定义您希望 genson 在序列化日期时使用的格式: Genson genson = new Genson.Builder().setDateFormat(yourDateFormat).create();

标签: java json


【解决方案1】:

我会稍微重构一下您的代码。首先,您想要一个 POCO 类作为您的数据载体,这将是您的项目类。然后你会想要一个 Repository 为你做 CRUD 操作。

public interface IItemModelRepository {
 Item getItemByName();
 Item getItemByDateCreated();
}

您可以将接口实现为:

public class ItemModelRepository : IItemModelRepository {

private IEnumerable<Item> _items;

public ItemModelRepository()
{
   this._items = /// Logic to load all items from database or from in memory cache
} 

public Item getItemByName(string name){
  return this._items.First(i => i.Name.Equals(name);
}

public Item getItemByDateCreated(DateTime dateCreated){
  return this._items.First(i => i.DateCreated == dateCreated);     
}

....
}

然后你可以使用这个 Repository(直接在 Controller 中使用 Repository 不是一个好主意。你宁愿返回一个 ViewModel,它是一个包含视图所需属性的 POCO 类):

var item = ItemModelRespository.GetByName("enter name here");
var viewModel = new ViewModel();
var serializer = new JavaScriptSerializer();
viewModel.JsonData = serializer.Serialize(item );

在客户端,您可以使用:

<%= Model.JsonData %>

也请看这里:ASP.NET MVC: How to convert View Model into Json object

--编辑

抱歉,错过了您使用 Java 的部分。如此处所述:

http://flax.ie/flax-engine-gwt-client-side-json-serialization-and-deserialization/

您可以将现有的项目类修改为:

public class Item implements JsonSerializable {

  private String name;
  private Date created;
  private Date updated;

 ..setters and getters

/**
* Pass this method JSON and it gives you back an object which you can
* then assign to your object via MyObject x = JsonToObject(String Json);
*
* @param JSON
* @return
*/
public static Item JsonToObject(String Json) {
    Serializer serializer = (Serializer) GWT.create(Serializer.class);
    return (Item)serializer.deSerialize(Json,"your fully qualified Item assembly name");
}

/**
* Creates a JSON string from the current object
*
* @return String of JSON
*/
public String toJson() {
    Serializer serializer = (Serializer) GWT.create(Serializer.class);
    return serializer.serialize(this);
}
}

然后在使用 IItemModelRepository 的代码中,您可以使用:

var item = ItemModelRepository.GetItemByName("name");
var jsonString = item.ToJson();

我不明白的是,当您希望 JSON 输出成为 Item 对象的序列化表示时,为什么要序列化 ​​ItemModel?

【讨论】:

  • 感谢您的回复。您的解决方案是特定于 .NET 的。我正在寻找特定于 java 的解决方案。最接近我的问题的潜在解决方案是使用 FlexJson 转换器,但我还没有完全掌握“转换器”概念的窍门。
  • 检查我更新的答案。我不清楚你的要求
  • 再次感谢。为了首先解决您的问题,我编写了一个名为 commentit (code.google.com/p/commentit) 的库,我正在试一试。我遇到了一种情况,我希望 ViewModel 对象有一个通用接口,以将它们与域(数据库)对象分离——这样它们就可以发展以满足客户的要求。我在服务器端使用模板框架将 html 与模型对象合并 - 在这里调用接口方法很容易。我还在客户端使用 JSON。我想为 JSON 和模板框架维护相同的接口
  • 当您说要将 ViewModel 与 Domain 解耦时,您是否打算使用某种映射层将域对象展平/投影到 ViewModel?
  • 所以我希望项目保持原样。我想将它包装在一个界面中,以便我可以“过滤”出我想要的东西,并且不希望对表示层和客户端可见。我希望我的模板框架只关心客户端的需求,而不知道数据库对象的样子。我想在需要时将这个相同的模型对象(包装在“范围限制”接口内的域对象)序列化为 JSON。这样我只需要维护一组 ViewModel 对象。我希望这能让你更好地了解我的意图
【解决方案2】:

您可以尝试使用 genson 库,它提供了几种方法来解决您的问题http://code.google.com/p/genson/

如果所需的输出是 {name: '', dateCreated : '', timeCreated : '', lastUpdated : '', lastUpdatedTime : ''} 没有“item :”:

public class ItemModel implements Model {    
    @JsonIgnore private final Item item; 
    ....
}

new Genson().serialize(myItemModel);

如果您想要“item”键,您还可以像在wiki 中那样实现自定义序列化器或转换器。

【讨论】:

    【解决方案3】:

    您可以使用 Jettison 之类的库手动定义自己的 JSON 映射,这样您就不必用 POJOS 和 POCO 以及包装器和其他愚蠢的东西来扭曲自己。设置映射确实不需要太多时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 2022-11-07
      • 2014-06-10
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多