【问题标题】:Rendering Hibernated objects in JSON?在 JSON 中渲染休眠对象?
【发布时间】:2015-06-05 18:36:51
【问题描述】:

我发现的所有关于 JSON 的网络文章似乎都与数据规范有关;我正在寻找有关清晰实施方式的提示。

我有一个从 Hibernate 获得的持久对象和一个应该返回它们列表的 Web 服务。

生成的 JSON 应该包含与数据混合的 HATEOAS 链接。 JSON 库不应触发任何错误(解决任何代理),而是将它们转换为 href 属性。

所以,如果我有 2 个 Customer 实例和一个名为 /customers 的 Web 服务,我应该得到如下所示的 JSON:

{
    "href" : "/customers",
    "data" : [  {   "id" : 234,
                    "name" : "Bill Shatner",
                    "href" : "/customers/234",
                    "orders" : "/customers/234/orders",
                    "orders-count" : 2
                },
                {
                    "id" : 210,
                    "name" : "Malcolm Reynolds",
                    "href" : "/customers/210",
                    "orders" : "",
                    "orders-count" : 0
                } ]
}

(注意:只是一个插图,没有按照 HAL 等任何规范进行编码)

我觉得我必须在服务中手动滚动所有这些东西,这感觉就像缺乏经验。肯定有一个代理感知的网络框架可以让我创建一个 JSON 模板并将它传递给我的域对象并期望得到正确的结果?

@Path("/customers")
public HttpResponse getAllCustomers(HttpRequest req)
{
    @Autowired CustomerRepository custRepo;
    List<Customer> data = custRepo.findAll();
    return ResponseBuilder.status(200).toJSON(data).build();
}

我知道这可能比可能的更神奇,但肯定有比为每个持久对象创建一个 HashMap、将每个 getter 硬编码到一个 put 并在一个大的 galumping 中添加硬编码的 href 字符串更好的东西环形?

【问题讨论】:

  • 那么你是说你想以 JSON 格式返回 List&lt;Customer&gt; data 吗?如果是,那么只需将 Spring 请求映射的返回类型设置为 @ResponseBody List&lt;Customer&gt; 。在类路径中包含 Jackson 库。您的列表将作为 JSON 响应返回,您可以找到更多详细信息 here
  • @Amit.rk3,这是否添加了 self 和 lazy has-a 的 URL?它是否避免解析代理?
  • 刚刚找到了更好的方法。将其发布在答案中。我认为这应该适合你

标签: java json spring hibernate


【解决方案1】:

实际上在普通的 JSON 支持之上,Spring 提供了一种简单的方法来实现基于 HATEOAS 的 rest 服务。看看这个link。看看Greetings 模型类。 import org.springframework.hateoas.ResourceSupport; 。如果您从 ResourceSupport 扩展您的 POJO 类。一些静态方法可以让您在正常的 JSON 响应之上添加这些 href。

【讨论】:

  • 我在搜索时看到了这个,但它似乎是“如果你想使用它,请放弃所有现有工作”的解决方案之一。依赖堆栈很大,并且没有集成到现有的 Spring 解决方案中(我担心它会破坏已经存在的其他东西)。
  • 另外,这个例子并不真实:没有处理/返回 HTTP 状态代码的规定。 Greeting 被过度注释:它几乎与在每个 Domain 对象上硬编码 toJson() 方法相同。另外它破坏了 SOC:域对象应该(确实)不知道视图表示(json 流)。这会迫使我的 DAL jar 中的依赖项发生巨大(不受欢迎的)变化。
【解决方案2】:

我使用 Gson lib 实现 Json Converter,因此您不仅可以与所有对象一起使用 Hibernate,而且在您的情况下,您可以创建抽象类或接口,并使所有休眠对象实现此接口,请查看此示例:

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JsonConvertor {

    private static GsonBuilder gsonBuilder;
    private static Gson gson;

    private JsonConvertor() {

    }
    public static Object fromJson(String json, Class clz)
    {
        gson=new Gson();
        return gson.fromJson(json,clz);
    }

    public static String toJson(Object obj,List<String> fields) {

        gsonBuilder = new GsonBuilder();
        gsonBuilder = gsonBuilder
                .addSerializationExclusionStrategy(new CustomIclusionStrategy(fields));
        gson = gsonBuilder.create();
        String json = gson.toJson(obj);
        return json;
    }
    public static String toJson(Object obj) {

        gsonBuilder = new GsonBuilder();
        gsonBuilder = gsonBuilder
                .addSerializationExclusionStrategy(new CustomIclusionStrategy(null));
        gson = gsonBuilder.create();
        String json = gson.toJson(obj);
        return json;
    }

}

class CustomIclusionStrategy implements ExclusionStrategy {

    private List<String> fields;

    public CustomIclusionStrategy(List<String> fields) {
        this.fields = fields;
    }

    // called only if shouldSkipClass returns false
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        if(fields!=null && fields.contains(f.getName()))
        {// exclude
            return true;
        }
        // include in generating JSON
        return false;


    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        // if returns false shouldSkipField will be called, otherwise shouldSkipField will not be called
        return false;
    }
}

这是界面:

public interface JsonConvertable {


    public String toJson();
}

这是示例对象:

public class B {


    private int salary;

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }


}
 public class A {

    private int id;
    private String name;
    private B b;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }   
}

这是一个如何从生成 JSON 中排除字段的示例。

public class Test {


    public static void main(String[] args) {
        A a=new A();

        a.setId(1);
        a.setName("safwa");
        a.setB(new B());
        List<String> fields=new ArrayList<String>();
        fields.add("b");
        System.out.println("Exclusion: "+JsonConvertor.toJson(a,fields));
        System.out.println("NO Exclusion: "+JsonConvertor.toJson(a));
    }
}

【讨论】:

  • 您的答案的核心似乎是排除策略。 Gson.toJson(Object) 没有深度控制,因此它将循环所有代理并解决它们。它也不会为 self 和包含列表添加 URL - 你会怎么做?您将如何实施 ExclusionStrategy 以避免解析代理?
  • 解决代理是什么意思,能不能补充一下这个问题的例子,让我们解决
  • 代理本质上是一个部分膨胀的、延迟获取的持久对象:它只有 'id' 值并且会导致数据库查询(或者 3,如果我的日志文件可信的话)对于任何其他值。解决它们意味着执行数据库查询以获取所有值。通常代理本身有关系,所以如果没有深度控制,就像在 Gson 中一样,你最终可以为单行 5 列实现一半的数据库!
  • 所以你需要深入,为相关的对象生成 JSON?
  • 不,Gson 默认会深入。我希望它这样做,只需返回 5 个值和一个指向代理的链接及其 id。我不希望它导致更多的数据库工作。
猜你喜欢
  • 2020-06-10
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-01
  • 2020-06-12
相关资源
最近更新 更多