3

以下是基于 Spring web MVC 3.1.1 的项目的片段。Json 序列化是通过 Jackson 完成的。

我有一个映射到 URL 的控制器,一切正常。

@Controller
@RequestMapping("/vod")
public class VODController {
 private Configuration configuration;
 private SearchAPI     searchAPI;

 @RequestMapping(method = RequestMethod.GET, params = "cmd=list")
 public @ResponseBody GetAssetsReply listVODAssets(long offset, int limit) {
    SearchVODAssetRequest searchVODAssetRequest = new SearchVODAssetRequest();
    //.... some irrelevant code
    return searchAPI.searchVODAssets(searchVODAssetRequest);
 }
}

这是GetAssetsReply

public class GetAssetsReply  {
    private long totalAssets;
    private List<VODAsset> assets = new LinkedList<VODAsset>();

    // Getters and setters removed for simplicity
}

VODAsset是一个接口:

public interface VODAsset {
    public String getName();
}

这是它的实现:

public class AssetElement implements VODAsset {
    private String     id;
    private String     name;
    private double     duration;

    // Getters and setters removed for simplicity
}

最后是问题:控制器向我返回了一个不利的预期结果 - 除了名称之外,它还返回 VOD 资产及其 ID 和持续时间。由于对象是由上面的VODAsset接口指向的,我期望得到的只是名称。我怎样才能得到这种行为?任何帮助将非常感激

4

4 回答 4

4

如果我正确理解您的问题并且您正在使用Jackson将结果转换为 JSON,那么您可以使用org.codehaus.jackson.annotate.JsonIgnore来避免该字段被填充为 JSON 结果。(Henry) 此外,可以添加到接口@JsonAutoDetect(JsonMethod.NONE)中,这会导致 Jackson 不自动搜索要序列化@JsonProperty的字段,然后添加确实需要序列化的字段(实际上实现了 Jackson 字段序列化策略的白名单方案)。

这是解决上述问题的示例代码:

@JsonAutoDetect(JsonMethod.NONE) //This tells the json serializer not to search for properties to serialize
public interface VODAsset {
    @JsonProperty //This tells the json serializer that this is a property that it should serialize
    public String getName();
}

另一方面,每个字段忽略方案可以通过以下方式实现:

标记注释,指示带注释的方法或字段将被基于自省的序列化和反序列化功能忽略。也就是说,它不应被视为“getter”、“setter”或“creator”。

@JsonIgnore
public String getId() {
    return id;
}
于 2012-06-03T15:49:24.290 回答
2

上面的答案(使用@JsonProperty 和@JsonIgnore)将您的数据模型与特定用例耦合,如果您需要返回此对象的不同投影(例如 id 属性),则意味着创建另一个类或更改注释并重新编译这个类。

我通常更喜欢返回一个我为每个用例显式填充的地图。例如

Map<String, Object> responseBody = new HashMap<String, Object>();
/*
JSON format: 
{
    "prop1":"<value1>",
    "edition":"<value2>",
    "dateProp":"<formatted date>"
}
*/
responseBody.put("prop1", value1);
responseBody.put("prop2", value2);
responseBody.put("dateProp", dateFormat.format(expirationDate));
return new ResponseEntity<Map<String, Object>>(responseBody, HttpStatus.OK);

当然,您也可以使用 @ResponseBody 而不是显式创建 ResposeEntity。

于 2012-06-03T18:51:08.690 回答
1

我使用委托代理创建了一个通用解决方案。这解决了多个问题:

  • 您可以在同一个实现上拥有不同的接口,这些接口公开同一对象的不同细节(例如,IPersonList 用于集合,IPersonDetail 扩展 IPersonList 以获取详细信息。
  • 您不需要在实体类中使用 @Json... 注释

示例代码(使用 commons-proxy):

/**
 * Wraps an object as delegating proxy, exposing only the methods of the interface
**/
@SuppressWarnings("unchecked")
protected <T> T wrap(Object sourceObject, Class<T> targetInterface)
{
    return (T) new ProxyFactory().createDelegatorProxy(
            new ConstantProvider(sourceObject),
            new Class[] {targetInterface});
}

现在你可以使用

return wrap(assetElement, VODAsset.class)

获取元素的 VODAsset 类型的“视图”。

下一步是创建一个拦截代理来检查 getter 的返回值。其中一个是接口,它还必须为返回值创建一个代理。还需要对列表进行特殊处理(例如,对于您的 VODAsset 列表示例)。所以你必须递归地创建代理。

在创建 100 个代理作为 jvm“预热”之后,我通过在循环中创建 1.000.000 个代理进行了一些性能测试:

  • Java 反射代理(使用 ProxyFactory):~1200ms
  • CGLib 代理(使用 CglibProxyFactory):~3900ms
  • Javassist 代理(使用 JavassistProxyFactory):~470ms

因此,使用 Javaasist 作为代理工厂,创建此类代理的开销适中。

于 2013-09-24T09:23:02.230 回答
0

实现,即类是根据你的 Spring bean 配置返回的。如果您想要不同的结果,请返回不同的实现。因此,如果 AssetElement 具有您不想公开的道具,请创建另一个实现您的界面并仅返回该道具的类。在您的 GetAssetsReply 中使用该 impl。

于 2012-06-03T15:39:11.587 回答