如何使用 Java 和 Gson 中的构建器模式将选择类字段序列化为 JSON 字符串?

Posted

技术标签:

【中文标题】如何使用 Java 和 Gson 中的构建器模式将选择类字段序列化为 JSON 字符串?【英文标题】:How to serialize select class fields to JSON string using the Builder Pattern in Java and Gson? 【发布时间】:2021-09-19 00:58:19 【问题描述】:

我创建了一个带有 Builder Pattern 的 User 类,目的是将其序列化为 JSON 字符串以用于 POST。现在,需求发生了变化,我需要能够修补现有记录,更新一个或多个字段,但不是更新记录集中的所有字段。下面的示例在其 User 类中有 5 个字段,但想象一下,如果它有 30 甚至 40 个字段,包括 int 类型。

import com.google.gson.*;

class User 

    //All final attributes
    private final String firstName;
    private final String lastName;
    private final int age;
    private final String phone;
    private final String address;
 
    private User(UserBuilder builder) 
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    
 
    //All getter, and NO setter to provide immutability
    public String getFirstName() 
        return firstName;
    
    public String getLastName() 
        return lastName;
    
    public int getAge() 
        return age;
    
    public String getPhone() 
        return phone;
    
    public String getAddress() 
        return address;
    
 
    public static class UserBuilder 
    
        private String firstName;
        private String lastName;
        private int age;
        private String phone;
        private String address;
 
        public UserBuilder() 
        

        public UserBuilder(String firstName, String lastName) 
            this.firstName = firstName;
            this.lastName = lastName;
        

        public UserBuilder firstName(String firstName) 
            this.firstName = firstName;
            return this;
        
        
        public UserBuilder lastName(String lastName) 
            this.firstName = lastName;
            return this;
        
        public UserBuilder age(int age) 
            this.age = age;
            return this;
        
        public UserBuilder phone(String phone) 
            this.phone = phone;
            return this;
        
        public UserBuilder address(String address) 
            this.address = address;
            return this;
        
        //Return the finally consrcuted User object
        public User build() 
            User user =  new User(this);
            return user;
        
    


public class TestUserBuild 

    public static void main(String[] args) 
        // TODO Auto-generated method stub
        User user = new User.UserBuilder().
        //No last name
        //No age
        //No phone
        //no address
        .firstName("Super")
        .build();
                 
        System.out.println(user); // User: Super, null, 0, null, null
                    
        Gson gson = new Gson();
                    
        System.out.println(gson.toJson(user)); // "firstName":"Super","age":0
    

我没有指定年龄,但它在 JSON 字符串中。我认为构建器模式将有助于创建任意数量的 JSON 字符串排列,即更新名字和姓氏,仅更新名字,仅更新年龄,仅更新姓氏和电话号码等...

建造者模式方法不是解决这个问题的正确方法吗?如果这是一个可接受的解决方案,我如何利用构建器模式将 User 类序列化为 JSON 字符串,但只有我选择的字段?我可以在 Gson 库中利用某些东西来实现此任务,例如创建自定义类型适配器吗?也许我可以创建一个自定义类型适配器,将所有字段作为输入,检查每个字段是否为 NULL,或者为整数为 0,然后仅使用增量构建 JSON 字符串。

【问题讨论】:

【参考方案1】:

您的“年龄”字段值 0,因为“int”具有默认值。如果您希望年龄字段默认为空,请使用“整数”。

Builder 模式的一些优点是不变性(您可以选择只允许构建器在创建时修改类,从类中删除所有 setter),并且它是更简洁用于实例化具有多个属性的类。

但您不需要保持您的 Builder 模式不可变。如果我正确理解您的需求,您可以在对象创建期间保留构建器模式以实现多功能性,并保留类上的设置器以便能够轻松更新字段。

import com.google.gson.*;

class User 
//Your attributes don't need to be final
private String firstName;
private String lastName;
private int age;
private String phone;
private String address;

private User(UserBuilder builder) 
    this.firstName = builder.firstName;
    this.lastName = builder.lastName;
    this.age = builder.age;
    this.phone = builder.phone;
    this.address = builder.address;


//getters AND setters (omitted for brevity)

//builder class stays as is, omitted for brevity

public class TestUserBuild 

    public static void main(String[] args) 
        User user = new User.UserBuilder()
        .firstName("Super")
        .build();
                 
        //when you need to update
        user.setAge(42);
    

问题是,如果您需要定期更新字段,为什么还要保持不变性?您是否应该完全删除不变性约束(通过将设置器留在类中)?您是否需要能够根据特定的业务规则更新特定的字段组合?如果后者是真的,我建议远离anemic model(“getter 和 setter 包”)并添加负责更新相关字段的特定于域的方法。您的 User 类将如下所示:

class User 
    //Your attributes don't need to be final
    private String firstName;
    private String lastName;
    private int age;
    private String phone;
    private String address;

    private User(UserBuilder builder) 
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    
    //getters ONLY (omitted for brevity)
    //no setters, only domain-relevant methods which update fields as needed
    public void setIdentity(String firstName, String lastName) 
        this.firstName = firstName;
        this.lastName = lastName;
    
    public void setCoordinates(String phone, String address) 
        this.phone = phone;
        this.address = address;
    

//builder class stays as is

public class TestUserBuild 

    public static void main(String[] args) 
        User user = new User.UserBuilder()
        .firstName("Super")
        .build();

        //update identity (say, your frontend has an "identity" page with only firstName and lastName on it
        user.setIdentity("Chris", "Neve");
        //your frontend page allowing user to update coordinates
        user.setCoordinates("+331231231", "7th av, NYC");
    

【讨论】:

您的帮助。我可能有 10 个“数字”字段,因此必须将它们转换为整数或类似的字段以尊重 NULL。我不需要 Builder 类是不可变的,但我已经看到了很多这样的例子,所以我尝试遵循最佳实践。是的,我需要能够灵活地根据业务规则为 PATCH 请求更新一个或多个字段。我将查看贫血模型链接并应用您的代码更改,ty。我将我的用例设想为一个 API 请求,因此我希望能够灵活地将 User 对象动态序列化为只有 delta 的 JSON 字符串。

以上是关于如何使用 Java 和 Gson 中的构建器模式将选择类字段序列化为 JSON 字符串?的主要内容,如果未能解决你的问题,请参考以下文章

gson中的java.lang.StackOverflowError

Java设计模式:Builder(构建器)模式

java Gson没有使用实例初始化器!

如何设计一个复杂的图形用户界面,同时使用构建器设计模式和抽象设计模式?

Java开发中的23种设计模式详解----构建器模式(Builder)

Java开发中的23种设计模式详解----构建器模式(Builder)