Spring MVC에서 JSON으로 보내는 동안 Java 객체의 필드를 동적으로 무시


105

최대 절전 모드를 위해 이와 같은 모델 클래스가 있습니다.

@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;
    }
}

Spring MVC 컨트롤러에서 DAO를 사용하여 객체를 얻을 수 있습니다. 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;
    }
}

보기 부분은 AngularJS를 사용하여 수행되므로 다음과 같이 JSON을 얻습니다.

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

암호화 된 암호를 설정하지 않으려면 해당 필드도 null로 설정합니다.

그러나 나는 이것을 원하지 않으며 모든 필드를 클라이언트 측에 보내고 싶지 않습니다. password, updatedby, createdby 필드를 보내지 않으려면 결과 JSON은 다음과 같아야합니다.

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

다른 데이터베이스 테이블에서 오는 클라이언트로 보내지 않으려는 필드 목록입니다. 따라서 로그인 한 사용자에 따라 변경됩니다. 어떻게해야합니까?

제 질문을 받으 셨기를 바랍니다.


이 답변에 대해 어떻게 말 하시겠습니까? stackoverflow.com/a/30559076/3488143
이리나

이 정보가 도움이 될 수 있습니다. stackoverflow.com/questions/12505141/…
Musa

답변:


142

@JsonIgnoreProperties("fieldname")POJO에 주석을 추가하십시오 .

또는 @JsonIgnoreJSON을 역 직렬화하는 동안 무시하려는 필드의 이름 앞에 사용할 수 있습니다 . 예:

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

GitHub 예


63
동적으로 할 수 있습니까? POJO에 없습니까? 컨트롤러 클래스에서 할 수 있습니까?
iCode 2014

3
@iProgrammer : 여기에 원하는 것과 비슷합니다. stackoverflow.com/questions/8179986/…
user3145373 ツ

3
@iProgrammer : 여기에 매우 인상적인 답변 stackoverflow.com/questions/13764280/…
user3145373 ツ

11
비고 : @JsonIgnore이다 com.fasterxml.jackson.annotation.JsonIgnore없습니다 org.codehaus.jackson.annotate.JsonIgnore.
xiaohuo jul.

5
요청에서 읽는 동안과 응답을 보내는 동안 모두 무시됩니다. 요청 개체에서 해당 속성이 필요하기 때문에 응답을 보내는 동안에 만 무시하고 싶습니다. 어떤 아이디어?
zulkarnain shah

32

나는 내가 파티에 조금 늦었다는 것을 알고있다. 그러나 나는 실제로 이것도 몇 달 전에 만났다. 사용 가능한 모든 솔루션이 나에게 그다지 매력적이지 않았기 때문에 (믹신? ugh!) 결국이 프로세스를 더 깔끔하게 만들기 위해 새 라이브러리를 만들었습니다. 누구나 시도해보고 싶다면 https://github.com/monitorjbl/spring-json-view 에서 사용할 수 있습니다 .

기본 사용법은 매우 간단하며 다음 JsonView과 같이 컨트롤러 메서드에서 객체 를 사용합니다 .

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"));
}

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
감사합니다! 이것이 나를위한 길이었습니다. 다른 위치에 동일한 개체가있는 사용자 지정 JSON보기가 필요했고 @JsonIgnore가 작동하지 않습니다. 이 라이브러리를 사용하면 간단하게 완료 할 수 있습니다.
제프

2
내 코드를 더 깔끔하게 만들고 구현을 더 쉽게 만들었습니다. 감사합니다
anindis

@monitorjbl : 이것은 약간 벗어난 것입니다. 저는 json 뷰를 사용하고 그 목적을 해결했습니다. 하지만 java.util.Date 클래스 (런타임 / 컴파일 시간 오류 없음)에 대한 사용자 지정 serializer를 등록 할 수 없습니다. 문자열은 사용자 지정 serializer를 등록 할 수있었습니다.
Ninad

17

동적으로 할 수 있습니까?

보기 클래스 만들기 :

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

모델에 주석 달기

@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
}

컨트롤러에서 뷰 클래스 지정

@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);
    }

}

데이터 예 :

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

1
이것은 환상적이고 최소한의 대답입니다! @Configuration 주석이 달린 구성 요소에서 몇 개의 필드 만 JSON으로 반환하고 자동으로 포함되는 모든 내부 필드를 건너 뛰고 싶었습니다. 감사합니다!
stx

15

JsonProperty.Access.WRITE_ONLY속성을 선언하는 동안 액세스를 설정하여이를 수행 할 수 있습니다 .

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

12

@JsonInclude(JsonInclude.Include.NON_NULL)클래스와 @JsonIgnore암호 필드에 추가 (Jackson이 null 값을 직렬화하도록 강제) 합니다.

물론 @JsonIgnore이 특정 경우뿐만 아니라 항상 then을 무시하고 싶다면 createdBy 및 updatedBy에 설정할 수 있습니다.

최신 정보

POJO 자체에 주석을 추가하고 싶지 않은 경우 Jackson 's Mixin Annotations가 좋은 옵션입니다. 문서 확인


동적으로 할 수 있습니까? POJO에 없습니까? 컨트롤러 클래스에서 할 수 있습니까?
iCode 2014

POJO에 주석을 추가하고 싶지 않다는 뜻입니까?
geoand

때로는 모든 필드를 클라이언트 측에 보내고 싶을 수 있기 때문입니다. 때로는 필드가 거의 없습니다. 클라이언트 측에 보내야하는 필드는 컨트롤러 클래스의 데이터베이스에서만 가져옵니다. 그 후에는 어떤 필드를 무시할지 설정해야합니다.
iCode 2014

12

예, JSON 응답으로 직렬화되는 필드와 무시할 필드를 지정할 수 있습니다. 이것은 동적으로 속성 무시를 구현하기 위해 수행해야하는 작업입니다.

1) 먼저 com.fasterxml.jackson.annotation.JsonFilter의 @JsonFilter를 엔티티 클래스에 추가해야합니다.

import com.fasterxml.jackson.annotation.JsonFilter;

@JsonFilter("SomeBeanFilter")
public class SomeBean {

  private String field1;

  private String field2;

  private String field3;

  // getters/setters
}

2) 그런 다음 컨트롤러에서 MappingJacksonValue 객체를 생성하고 필터를 설정해야하며 결국이 객체를 반환해야합니다.

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;
  }
}

다음은 응답으로 얻을 수있는 것입니다.

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

대신 :

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

여기서는 field1 및 field2 속성을 제외한 다른 속성 (이 경우 field3)을 응답으로 무시하는 것을 볼 수 있습니다.

도움이 되었기를 바랍니다.


1
@Shafqat Man, 정말 감사합니다, 당신은 나의 구세주입니다. 이런 종류의 기능을 찾으려고 거의 하루를 보냈습니다. 이 솔루션은 너무 우아하고 간단합니까? 요청 된대로 정확히 수행합니다. 정답으로 표시되어야합니다.
Oleg Kuts

6

내가 너 였고 그렇게하고 싶었다면 컨트롤러 계층에서 내 사용자 엔터티를 사용하지 않고 대신 UserDto (데이터 전송 개체)를 만들어 사용하여 비즈니스 (서비스) 계층 및 컨트롤러와 통신합니다. Apache BeanUtils (copyProperties 메소드)를 사용하여 User 엔티티에서 UserDto로 데이터를 복사 할 수 있습니다.


3

응답을 제공하는 동안 런타임에 필드를 무시하는 데 사용할 수있는 JsonUtil을 만들었습니다.

사용 예 : 첫 번째 인수는 POJO 클래스 (Student) 여야하며 ignoreFields는 응답에서 무시하려는 쉼표로 구분 된 필드입니다.

 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

@JsonIgnore@kryger가 제안한 것처럼 만 사용하여 해결 했습니다. 따라서 게터는 다음과 같이됩니다.

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

@JsonIgnore물론 여기에 설명 된대로 필드, 세터 또는 게터에 설정할 수 있습니다 .

그리고 직렬화 측면에서만 암호화 된 암호를 보호하려면 (예 : 사용자 로그인이 필요한 경우) 다음 @JsonProperty주석을 필드에 추가 하십시오 .

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

여기에 더 많은 정보가 있습니다 .


1

나는 Spring과 jackson으로 나를위한 해결책을 찾았다.

먼저 엔티티에 필터 이름을 지정하십시오.

@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);

}

그런 다음 상수 필터 이름 클래스를 스프링 구성에 사용되는 기본 FilterProvider로 볼 수 있습니다.

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);
}

}

봄 구성 :

@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());

}

}

마지막으로 필요한 경우 restConstoller에서 특정 필터를 지정할 수 있습니다 ....

@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
왜 쉬운 질문에 대한 합병증 때문에
Humoyun 아마드

1

장소 @JsonIgnore필드에서 또는 게터, 또는 사용자 정의 DTO를 만들

@JsonIgnore
private String encryptedPwd;

또는 위에서 언급했듯이 액세스 속성이 쓰기 전용으로 설정된 곳에 ceekay주석을 달아@JsonProperty

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

0

UserJsonResponse클래스를 만들고 원하는 필드로 채우는 것이 더 깨끗한 솔루션이 아닐까요?

모든 모델을 되돌리고 싶을 때 JSON을 직접 반환하는 것은 훌륭한 솔루션으로 보입니다. 그렇지 않으면 그냥 지저분 해집니다.

예를 들어 앞으로 어떤 Model 필드와도 일치하지 않는 JSON 필드를 원할 수 있으며 더 큰 문제에 직면하게됩니다.


0

이것은 위의 답변에 대한 깨끗한 유틸리티 도구입니다 .

@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

엔티티 클래스에서 @JsonInclude(JsonInclude.Include.NON_NULL)주석을 추가 하여 문제를 해결하십시오.

그것은 다음과 같이 보일 것이다

@Entity
@JsonInclude(JsonInclude.Include.NON_NULL)

전혀 관련이 없습니다. 질문의 목적은 다르지만 대답은 다른 것에 대한 것입니다. -1
Hammad Dar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.