Bỏ qua các trường từ đối tượng Java động trong khi gửi dưới dạng JSON từ Spring MVC


105

Tôi có lớp mô hình như thế này, cho ngủ đông

@Entity
@Table(name = "user", catalog = "userdb")
@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements java.io.Serializable {

    private Integer userId;
    private String userName;
    private String emailId;
    private String encryptedPwd;
    private String createdBy;
    private String updatedBy;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "UserId", unique = true, nullable = false)
    public Integer getUserId() {
        return this.userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Column(name = "UserName", length = 100)
    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Column(name = "EmailId", nullable = false, length = 45)
    public String getEmailId() {
        return this.emailId;
    }

    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Column(name = "EncryptedPwd", length = 100)
    public String getEncryptedPwd() {
        return this.encryptedPwd;
    }

    public void setEncryptedPwd(String encryptedPwd) {
        this.encryptedPwd = encryptedPwd;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    @Column(name = "UpdatedBy", length = 100)
    public String getUpdatedBy() {
        return this.updatedBy;
    }

    public void setUpdatedBy(String updatedBy) {
        this.updatedBy = updatedBy;
    }
}

Trong bộ điều khiển Spring MVC, sử dụng DAO, tôi có thể lấy đối tượng. và trả về dưới dạng Đối tượng JSON.

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/getUser/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public User getUser(@PathVariable Integer userId) throws Exception {

        User user = userService.get(userId);
        user.setCreatedBy(null);
        user.setUpdatedBy(null);
        return user;
    }
}

Phần xem được thực hiện bằng AngularJS, vì vậy nó sẽ nhận được JSON như thế này

{
  "userId" :2,
  "userName" : "john",
  "emailId" : "john@gmail.com",
  "encryptedPwd" : "Co7Fwd1fXYk=",
  "createdBy" : null,
  "updatedBy" : null
}

Nếu tôi không muốn đặt Mật khẩu được mã hóa, tôi cũng sẽ đặt trường đó là null.

Nhưng tôi không muốn như vậy, tôi không muốn gửi tất cả các trường cho phía máy khách. Nếu tôi không muốn gửi mật khẩu, cập nhật, các trường đã tạo, thì kết quả JSON của tôi sẽ giống như

{
  "userId" :2,
  "userName" : "john",
  "emailId" : "john@gmail.com"
}

Danh sách các trường mà tôi không muốn gửi đến máy khách đến từ bảng cơ sở dữ liệu khác. Vì vậy, nó sẽ thay đổi dựa trên người dùng đã đăng nhập. Làm cách nào để làm điều đó?

Tôi hy vọng bạn đã nhận được câu hỏi của tôi.


Bạn sẽ nói gì về câu trả lời này? stackoverflow.com/a/30559076/3488143
Iryna

thông tin này có thể hữu ích stackoverflow.com/questions/12505141/…
Musa

Câu trả lời:


142

Thêm @JsonIgnoreProperties("fieldname")chú thích vào POJO của bạn.

Hoặc bạn có thể sử dụng @JsonIgnoretrước tên của trường mà bạn muốn bỏ qua trong khi giải mã JSON. Thí dụ:

@JsonIgnore
@JsonProperty(value = "user_password")
public String getUserPassword() {
    return userPassword;
}

Ví dụ về GitHub


63
Tôi có thể làm điều đó một cách năng động không? Không có trong POJO? Tôi có thể làm điều đó trong lớp Bộ điều khiển của mình không?
iCode

3
@iProgrammer: đây là một tương tự như bạn muốn: stackoverflow.com/questions/8179986/…
user3145373 ツ

3
@iProgrammer: câu trả lời rất ấn tượng ở đây stackoverflow.com/questions/13764280/…
user3145373 ツ

11
nhận xét: @JsonIgnorecom.fasterxml.jackson.annotation.JsonIgnorekhông org.codehaus.jackson.annotate.JsonIgnore.
xiaohuo

5
Điều đó bỏ qua cả khi đọc từ yêu cầu và trong khi gửi phản hồi. Tôi chỉ muốn bỏ qua trong khi gửi phản hồi vì tôi cần thuộc tính đó từ đối tượng yêu cầu. Bất kỳ ý tưởng?
zulkarnain shah

32

Tôi biết mình đến dự tiệc hơi muộn, nhưng thực sự tôi đã gặp phải vấn đề này vài tháng trước. Tất cả các giải pháp có sẵn đều không hấp dẫn đối với tôi (mixins? Ugh!), Vì vậy tôi đã kết thúc việc tạo một thư viện mới để làm cho quá trình này trở nên sạch sẽ hơn. Nó có sẵn ở đây nếu ai muốn dùng thử: https://github.com/monitorjbl/spring-json-view .

Cách sử dụng cơ bản khá đơn giản, bạn sử dụng JsonViewđối tượng trong các phương thức bộ điều khiển của mình như sau:

import com.monitorjbl.json.JsonView;
import static com.monitorjbl.json.Match.match;

@RequestMapping(method = RequestMethod.GET, value = "/myObject")
@ResponseBody
public void getMyObjects() {
    //get a list of the objects
    List<MyObject> list = myObjectService.list();

    //exclude expensive field
    JsonView.with(list).onClass(MyObject.class, match().exclude("contains"));
}

Bạn cũng có thể sử dụng nó bên ngoài Spring:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import static com.monitorjbl.json.Match.match;

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);

mapper.writeValueAsString(JsonView.with(list)
      .onClass(MyObject.class, match()
        .exclude("contains"))
      .onClass(MySmallObject.class, match()
        .exclude("id"));

5
Cảm ơn bạn! Đây là con đường để đi cho tôi. Tôi cần các chế độ xem JSON tùy chỉnh với các đối tượng giống nhau ở các vị trí khác nhau và @JsonIgnore sẽ không hoạt động. Thư viện này đã làm cho nó trở nên đơn giản.
Jeff

2
Bạn đã làm cho mã của tôi sạch hơn và việc triển khai dễ dàng hơn. cảm ơn bạn
anindis

@monitorjbl: cái này hơi chệch hướng, tôi đã sử dụng chế độ xem json và nó giải quyết mục đích của tôi. Nhưng tôi không thể đăng ký bộ tuần tự tùy chỉnh cho lớp java.util.Date (không có lỗi thời gian chạy / thời gian biên dịch) cũng như đối với chuỗi tôi có thể đăng ký bộ nối tiếp tùy chỉnh.
Ninad

17

Tôi có thể làm điều đó một cách năng động không?

Tạo lớp xem:

public class View {
    static class Public { }
    static class ExtendedPublic extends Public { }
    static class Internal extends ExtendedPublic { }
}

Chú thích mô hình của bạn

@Document
public class User {

    @Id
    @JsonView(View.Public.class)
    private String id;

    @JsonView(View.Internal.class)
    private String email;

    @JsonView(View.Public.class)
    private String name;

    @JsonView(View.Public.class)
    private Instant createdAt = Instant.now();
    // getters/setters
}

Chỉ định lớp xem trong bộ điều khiển của bạn

@RequestMapping("/user/{email}")
public class UserController {

    private final UserRepository userRepository;

    @Autowired
    UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @RequestMapping(method = RequestMethod.GET)
    @JsonView(View.Internal.class)
    public @ResponseBody Optional<User> get(@PathVariable String email) {
        return userRepository.findByEmail(email);
    }

}

Ví dụ dữ liệu:

{"id":"5aa2496df863482dc4da2067","name":"test","createdAt":"2018-03-10T09:35:31.050353800Z"}

1
Đây là một câu trả lời tuyệt vời và tối giản! Tôi chỉ muốn trả lại dưới dạng JSON chỉ một số trường từ thành phần được chú thích @Configuration và bỏ qua tất cả các trường bên trong được tự động đưa vào. Cảm ơn rất nhiều!
stx

15

Chúng ta có thể làm điều này bằng cách đặt quyền truy cập JsonProperty.Access.WRITE_ONLYtrong khi khai báo thuộc tính.

@JsonProperty( value = "password", access = JsonProperty.Access.WRITE_ONLY)
@SerializedName("password")
private String password;

12

Thêm @JsonInclude(JsonInclude.Include.NON_NULL)(buộc Jackson phải tuần tự hóa các giá trị null) vào lớp cũng như@JsonIgnore vào trường mật khẩu.

Tất nhiên bạn cũng có thể đặt @JsonIgnoretrên createBy và updatedBy nếu bạn luôn muốn bỏ qua sau đó và không chỉ trong trường hợp cụ thể này.

CẬP NHẬT

Trong trường hợp bạn không muốn thêm chú thích vào chính POJO, thì một lựa chọn tuyệt vời là Jackson's Mixin Annotations. Kiểm tra tài liệu


Tôi có thể làm điều đó một cách năng động không? Không có trong POJO? Tôi có thể làm điều đó trong lớp Bộ điều khiển của mình không?
iCode

Ý của bạn là bạn không muốn thêm chú thích vào POJO?
geoand

Bởi vì Đôi khi tôi có thể muốn gửi tất cả các trường cho phía máy khách. Đôi khi ít lĩnh vực. Các trường mà tôi nên gửi đến phía máy khách chỉ nhận được từ cơ sở dữ liệu trong lớp bộ điều khiển. Sau đó, tôi chỉ cần đặt trường nào nên bỏ qua.
iCode

12

Có, bạn có thể chỉ định trường nào được tuần tự hóa dưới dạng phản hồi JSON và trường nào cần bỏ qua. Đây là những gì bạn cần làm để triển khai các thuộc tính Động lực bỏ qua.

1) Đầu tiên, bạn cần thêm @JsonFilter từ com.fasterxml.jackson.annotation.JsonFilter trên lớp thực thể của bạn dưới dạng.

import com.fasterxml.jackson.annotation.JsonFilter;

@JsonFilter("SomeBeanFilter")
public class SomeBean {

  private String field1;

  private String field2;

  private String field3;

  // getters/setters
}

2) Sau đó, trong bộ điều khiển của bạn, bạn phải thêm tạo đối tượng MappingJacksonValue và đặt các bộ lọc trên nó và cuối cùng, bạn phải trả lại đối tượng này.

import java.util.Arrays;
import java.util.List;

import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

@RestController
public class FilteringController {

  // Here i want to ignore all properties except field1,field2.
  @GetMapping("/ignoreProperties")
  public MappingJacksonValue retrieveSomeBean() {
    SomeBean someBean = new SomeBean("value1", "value2", "value3");

    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2");

    FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter);

    MappingJacksonValue mapping = new MappingJacksonValue(someBean);

    mapping.setFilters(filters);

    return mapping;
  }
}

Đây là những gì bạn sẽ nhận được khi phản hồi:

{
  field1:"value1",
  field2:"value2"
}

thay vì cái này:

{
  field1:"value1",
  field2:"value2",
  field3:"value3"
}

Ở đây, bạn có thể thấy nó bỏ qua các thuộc tính khác (trường 3 trong trường hợp này) để phản hồi ngoại trừ thuộc tính field1 và field2.

Hi vọng điêu nay co ich.


1
@Shafqat Man, cảm ơn bạn rất nhiều, bạn là vị cứu tinh của tôi. Đã dành gần một ngày để cố gắng tìm ra loại chức năng này. Giải pháp này rất thanh lịch và đơn giản? và thực hiện chính xác những gì được yêu cầu. Nên được đánh dấu là câu trả lời đúng.
Oleg Kuts

6

Nếu tôi là bạn và muốn làm như vậy, tôi sẽ không sử dụng thực thể Người dùng của mình trong lớp Bộ điều khiển mà thay vào đó tôi tạo và sử dụng UserDto (Đối tượng truyền dữ liệu) để giao tiếp với lớp nghiệp vụ (Dịch vụ) và Bộ điều khiển. Bạn có thể sử dụng Apache BeanUtils (phương thức copyProperties) để sao chép dữ liệu từ thực thể Người dùng sang UserDto.


3

Tôi đã tạo một JsonUtil có thể được sử dụng để bỏ qua các trường trong thời gian chạy trong khi đưa ra phản hồi.

Ví dụ Cách sử dụng: Đối số đầu tiên phải là bất kỳ lớp POJO nào (Sinh viên) và Trường bỏ qua là các trường được phân tách bằng dấu phẩy mà bạn muốn bỏ qua trong phản hồi.

 Student st = new Student();
 createJsonIgnoreFields(st,"firstname,age");

import java.util.logging.Logger;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.ser.FilterProvider;
import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider;

public class JsonUtil {

  public static String createJsonIgnoreFields(Object object, String ignoreFields) {
     try {
         ObjectMapper mapper = new ObjectMapper();
         mapper.getSerializationConfig().addMixInAnnotations(Object.class, JsonPropertyFilterMixIn.class);
         String[] ignoreFieldsArray = ignoreFields.split(",");
         FilterProvider filters = new SimpleFilterProvider()
             .addFilter("filter properties by field names",
                 SimpleBeanPropertyFilter.serializeAllExcept(ignoreFieldsArray));
         ObjectWriter writer = mapper.writer().withFilters(filters);
         return writer.writeValueAsString(object);
     } catch (Exception e) {
         //handle exception here
     }
     return "";
   }

   public static String createJson(Object object) {
        try {
         ObjectMapper mapper = new ObjectMapper();
         ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
         return writer.writeValueAsString(object);
        }catch (Exception e) {
         //handle exception here
        }
        return "";
   }
 }    

2

Tôi đã giải quyết chỉ bằng cách sử dụng @JsonIgnore@kryger đã đề xuất. Vì vậy, getter của bạn sẽ trở thành:

@JsonIgnore
public String getEncryptedPwd() {
    return this.encryptedPwd;
}

Bạn có thể thiết lập @JsonIgnorekhóa học trên sân, setter hoặc getter như mô tả ở đây .

Và, nếu bạn chỉ muốn bảo vệ mật khẩu được mã hóa ở phía tuần tự hóa (ví dụ: khi bạn cần đăng nhập người dùng của mình), hãy thêm @JsonPropertychú thích này vào trường của bạn :

@JsonProperty(access = Access.WRITE_ONLY)
private String encryptedPwd;

Thêm thông tin ở đây .


1

Tôi đã tìm ra giải pháp cho mình với Spring và jackson

Đầu tiên chỉ định tên bộ lọc trong thực thể

@Entity
@Table(name = "SECTEUR")
@JsonFilter(ModelJsonFilters.SECTEUR_FILTER)
public class Secteur implements Serializable {

/** Serial UID */
private static final long serialVersionUID = 5697181222899184767L;

/**
 * Unique ID
 */
@Id
@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "code", nullable = false, length = 35)
private String code;

/**
 * Identifiant du secteur parent
 */
@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "id_parent")
private Long idParent;

@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "id_parent")
private List<Secteur> secteursEnfants = new ArrayList<>(0);

}

Sau đó, bạn có thể thấy lớp tên bộ lọc hằng số với FilterProvider mặc định được sử dụng trong cấu hình mùa xuân

public class ModelJsonFilters {

public final static String SECTEUR_FILTER = "SecteurFilter";
public final static String APPLICATION_FILTER = "ApplicationFilter";
public final static String SERVICE_FILTER = "ServiceFilter";
public final static String UTILISATEUR_FILTER = "UtilisateurFilter";

public static SimpleFilterProvider getDefaultFilters() {
    SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAll();
    return new SimpleFilterProvider().setDefaultFilter(theFilter);
}

}

Cấu hình mùa xuân:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "fr.sodebo")

public class ApiRootConfiguration extends WebMvcConfigurerAdapter {

@Autowired
private EntityManagerFactory entityManagerFactory;


/**
 * config qui permet d'éviter les "Lazy loading Error" au moment de la
 * conversion json par jackson pour les retours des services REST<br>
 * on permet à jackson d'acceder à sessionFactory pour charger ce dont il a
 * besoin
 */
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

    super.configureMessageConverters(converters);
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();

    // config d'hibernate pour la conversion json
    mapper.registerModule(getConfiguredHibernateModule());//

    // inscrit les filtres json
    subscribeFiltersInMapper(mapper);

    // config du comportement de json views
    mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

    converter.setObjectMapper(mapper);
    converters.add(converter);
}

/**
 * config d'hibernate pour la conversion json
 * 
 * @return Hibernate5Module
 */
private Hibernate5Module getConfiguredHibernateModule() {
    SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
    Hibernate5Module module = new Hibernate5Module(sessionFactory);
    module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);

    return module;

}

/**
 * inscrit les filtres json
 * 
 * @param mapper
 */
private void subscribeFiltersInMapper(ObjectMapper mapper) {

    mapper.setFilterProvider(ModelJsonFilters.getDefaultFilters());

}

}

Cuối cùng, tôi có thể chỉ định một bộ lọc cụ thể trong restConstoller khi tôi cần ....

@RequestMapping(value = "/{id}/droits/", method = RequestMethod.GET)
public MappingJacksonValue getListDroits(@PathVariable long id) {

    LOGGER.debug("Get all droits of user with id {}", id);

    List<Droit> droits = utilisateurService.findDroitsDeUtilisateur(id);

    MappingJacksonValue value;

    UtilisateurWithSecteurs utilisateurWithSecteurs = droitsUtilisateur.fillLists(droits).get(id);

    value = new MappingJacksonValue(utilisateurWithSecteurs);

    FilterProvider filters = ModelJsonFilters.getDefaultFilters().addFilter(ModelJsonFilters.SECTEUR_FILTER, SimpleBeanPropertyFilter.serializeAllExcept("secteursEnfants")).addFilter(ModelJsonFilters.APPLICATION_FILTER,
            SimpleBeanPropertyFilter.serializeAllExcept("services"));

    value.setFilters(filters);
    return value;

}

5
tại sao rất phức tạp cho một câu hỏi dễ dàng
Humoyun Ahmad

1

Đặt @JsonIgnoretrên sân hoặc cửa sổ của nó, hoặc tạo một dto tùy chỉnh

@JsonIgnore
private String encryptedPwd;

hoặc như đã đề cập ở trên bằng cách ceekaychú thích nó với @JsonPropertynơi thuộc tính truy cập được đặt thành chỉ ghi

@JsonProperty( value = "password", access = JsonProperty.Access.WRITE_ONLY)
private String encryptedPwd;

0

Sẽ không tạo ra một UserJsonResponse lớp và điền vào các trường mong muốn có phải là giải pháp sạch hơn không?

Trả lại trực tiếp một JSON có vẻ là một giải pháp tuyệt vời khi bạn muốn trả lại tất cả mô hình. Nếu không nó chỉ trở nên lộn xộn.

Trong tương lai, chẳng hạn, bạn có thể muốn có trường JSON không khớp với bất kỳ trường Model nào và khi đó bạn sẽ gặp rắc rối lớn hơn.


0

Đây là một công cụ tiện ích rõ ràng cho câu trả lời trên :

@GetMapping(value = "/my-url")
public @ResponseBody
MappingJacksonValue getMyBean() {
    List<MyBean> myBeans = Service.findAll();
    MappingJacksonValue mappingValue = MappingFilterUtils.applyFilter(myBeans, MappingFilterUtils.JsonFilterMode.EXCLUDE_FIELD_MODE, "MyFilterName", "myBiggerObject.mySmallerObject.mySmallestObject");
    return mappingValue;
}

//AND THE UTILITY CLASS
public class MappingFilterUtils {

    public enum JsonFilterMode {
        INCLUDE_FIELD_MODE, EXCLUDE_FIELD_MODE
    }
    public static MappingJacksonValue applyFilter(Object object, final JsonFilterMode mode, final String filterName, final String... fields) {
        if (fields == null || fields.length == 0) {
            throw new IllegalArgumentException("You should pass at least one field");
        }
        return applyFilter(object, mode, filterName, new HashSet<>(Arrays.asList(fields)));
    }

    public static MappingJacksonValue applyFilter(Object object, final JsonFilterMode mode, final String filterName, final Set<String> fields) {
        if (fields == null || fields.isEmpty()) {
            throw new IllegalArgumentException("You should pass at least one field");
        }

        SimpleBeanPropertyFilter filter = null;
        switch (mode) {
            case EXCLUDE_FIELD_MODE:
                filter = SimpleBeanPropertyFilter.serializeAllExcept(fields);
                break;
            case INCLUDE_FIELD_MODE:
                filter = SimpleBeanPropertyFilter.filterOutAllExcept(fields);
                break;
        }

        FilterProvider filters = new SimpleFilterProvider().addFilter(filterName, filter);
        MappingJacksonValue mapping = new MappingJacksonValue(object);
        mapping.setFilters(filters);
        return mapping;
    }
}

-5

Trong lớp thực thể của bạn, hãy thêm @JsonInclude(JsonInclude.Include.NON_NULL) chú thích để giải quyết vấn đề

nó sẽ giống như

@Entity
@JsonInclude(JsonInclude.Include.NON_NULL)

Hoàn toàn trả lời không thể miễn cưỡng. Mục đích của câu hỏi là khác nhau trong khi câu trả lời là về một cái gì đó khác. -1 cho điều đó
Hammad Dar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.