Jackson đổi tên trường boolean nguyên thủy bằng cách xóa 'is'


94

Đây có thể là một bản sao. Nhưng tôi không thể tìm ra giải pháp cho vấn đề của mình.

Tôi có một lớp học

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Getters và setters được tạo bởi Eclipse.

Trong một lớp khác, tôi đặt giá trị thành true và viết nó dưới dạng chuỗi JSON.

System.out.println(new ObjectMapper().writeValueAsString(myResponse));

Trong JSON, điều quan trọng là {"success": true}.

Tôi muốn chìa khóa như isSuccesschính nó. Jackson có đang sử dụng phương thức setter trong khi tuần tự hóa không? Làm thế nào để đặt khóa chính là tên trường?


1
nếu tên thuộc tính của bạn là liek thì isSuccesstên phương thức phải là isIsSuccesstôi nghĩ
Jens

Tôi hiểu. Tôi nghĩ nó tốt hơn SetSuccess vì nó được tạo bởi Eclipse. (Theo tiêu chuẩn)
iCode

Câu trả lời:


120

Đây là một câu trả lời hơi muộn, nhưng có thể hữu ích cho bất kỳ ai khác đến trang này.

Một giải pháp đơn giản để thay đổi tên mà Jackson sẽ sử dụng khi tuần tự hóa thành JSON là sử dụng chú thích @JsonProperty , vì vậy ví dụ của bạn sẽ trở thành:

public class MyResponse implements Serializable {

    private boolean isSuccess;

    @JsonProperty(value="isSuccess")        
    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Điều này sau đó sẽ được tuần tự hóa thành JSON {"isSuccess":true}, nhưng có lợi thế là không phải sửa đổi tên phương thức getter của bạn.

Lưu ý rằng trong trường hợp này, bạn cũng có thể viết chú thích @JsonProperty("isSuccess")vì nó chỉ có một valuephần tử duy nhất


Phương thức này sẽ không hoạt động đối với trường hợp của tôi vì lớp không thuộc sở hữu của tôi vì nó đến từ các phụ thuộc của bên thứ ba. Đối với trường hợp như vậy, hãy xem câu trả lời của tôi bên dưới.
edmundpie

5
Tôi đang sử dụng Spring boot với jackson nhưng nhận được hai trường một là "thành công" và trường kia là "isSuccess" và khi tôi sử dụng Boolean không nguyên thủy thì chỉ có một trường "isSuccess"
Vishal Singla

@VishalSingla Tôi có cùng một vấn đề chính xác, giải pháp này tạo ra hai trường trong Spring Boot
Aron Fiechter

22

Gần đây tôi đã gặp phải vấn đề này và đây là những gì tôi tìm thấy. Jackson sẽ kiểm tra bất kỳ lớp nào mà bạn chuyển cho nó để tìm getters và setters, đồng thời sử dụng các phương pháp đó để tuần tự hóa và giải mã hóa. Những gì sau "get", "is" và "set" trong các phương thức đó sẽ được sử dụng làm khóa cho trường JSON ("isValid" cho getIsValid và setIsValid).

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }
} 

Tương tự, "isSuccess" sẽ trở thành "thành công", trừ khi được đổi tên thành "isIsSuccess" hoặc "getIsSuccess"

Đọc thêm tại đây: http://www.citrine.io/blog/2015/5/20/jackson-json-processor


6
isValid không đúng quy ước đặt tên cho kiểu dữ liệu boolean trong java. phải hợp lệ và isValid (), setValid ()
vels4j

2
nhưng nó không được cho là chính xác? Một quy ước ? Nếu nó tồn tại, bạn có thể liên kết đến tài liệu tham khảo Jackson nói rằng nó sử dụng tên getter làm trường JSON không? Hay bạn nghĩ đó là một lựa chọn thiết kế tồi?
Abhinav Vishak

2
Tôi ước có một cảnh báo cho điều này
RyPope

@ vels4j Quy ước đặt tên sẽ xuất hiện trong cửa sổ khi bạn đang xử lý các triển khai rất cụ thể.
Dragas

13

Sử dụng cả hai chú thích bên dưới, buộc JSON đầu ra phải bao gồm is_xxx:

@get:JsonProperty("is_something")
@param:JsonProperty("is_something")

Đây là câu trả lời tốt nhất cho câu hỏi này.
dustinevan

1
Đó có phải là Java không? Có lẽ đó là Kotlin?
spottedmahn

5

Bạn có thể định cấu hình của mình ObjectMappernhư sau:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            @Override
            public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
            {
                if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
                        && method.getName().startsWith("is")) {
                    return method.getName();
                }
                return super.nameForGetterMethod(config, method, defaultName);
            }
        });

1
Tôi thích rằng bạn đang cố gắng giải quyết vấn đề này thông qua cấu hình. Tuy nhiên, điều này sẽ chỉ hoạt động nếu bạn luôn đặt tiền tố các trường boolean và thuộc tính JSON của mình bằng "is". Giả sử bạn có một trường boolean khác được đặt tên đơn giản là "đã bật" mà bạn muốn tuần tự hóa như vậy. Vì phương thức được tạo là "isEnabled ()", đoạn mã trên sau đó sẽ tuần tự hóa nó thành "isEnabled" thay vì chỉ "enable". Cuối cùng, vấn đề là đối với cả hai trường "x" và "isX", Eclipse tạo ra phương thức "isX ()"; vì vậy bạn không thể suy ra tên thuộc tính khớp với trường.
David Siegal

@DavidSiegal dựa trên câu trả lời burak Tôi đã mở rộng câu trả lời bên dưới để hỗ trợ trường hợp như vậy.
edmundpie

4

Khi bạn đang sử dụng Kotlin và các lớp dữ liệu:

data class Dto(
    @get:JsonProperty("isSuccess") val isSuccess: Boolean
)

Bạn có thể cần phải thêm @param:JsonProperty("isSuccess")nếu bạn cũng định giải mã JSON.


2

Dựa trên câu trả lời của Utkarsh ..

Tên getter trừ get / được sử dụng làm tên JSON.

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

được lưu trữ dưới dạng {"harryPotter": "anythingYouGaveHere"}


Đối với Deserialization, Jackson kiểm tra cả setter và tên trường. Đối với chuỗi Json {"word1": "example"} , cả hai điều dưới đây đều hợp lệ.

public class Example{
    private String word1; 

    public setword2( String pqr){
        this.word1 = pqr; 
    }
}

public class Example2{
    private String word2; 

    public setWord1(String pqr){
        this.word2 = pqr ; 
    }
}

Một câu hỏi thú vị hơn là Jackson xem xét đơn đặt hàng nào cho việc khử không khí. Nếu tôi cố gắng deserialize {"word1": "myName"} với

public class Example3{
    private String word1;
    private String word2; 

    public setWord1( String parameter){
        this.word2 = parameter ; 
    }
}

Tôi đã không kiểm tra trường hợp trên, nhưng sẽ rất thú vị khi xem các giá trị của word1 & word2 ...

Lưu ý: Tôi đã sử dụng nhiều tên khác nhau để nhấn mạnh các trường bắt buộc phải giống nhau.


1

có một phương pháp khác cho vấn đề này.

chỉ cần xác định một lớp con mới mở rộng PropertyNamingStrategy và chuyển nó vào cá thể ObjectMapper.

đây là một đoạn mã có thể giúp ích nhiều hơn:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
        @Override
        public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
            String input = defaultName;
            if(method.getName().startsWith("is")){
                input = method.getName();
            }

            //copy from LowerCaseWithUnderscoresStrategy
            if (input == null) return input; // garbage in, garbage out
            int length = input.length();
            StringBuilder result = new StringBuilder(length * 2);
            int resultLength = 0;
            boolean wasPrevTranslated = false;
            for (int i = 0; i < length; i++)
            {
                char c = input.charAt(i);
                if (i > 0 || c != '_') // skip first starting underscore
                {
                    if (Character.isUpperCase(c))
                    {
                        if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                        {
                            result.append('_');
                            resultLength++;
                        }
                        c = Character.toLowerCase(c);
                        wasPrevTranslated = true;
                    }
                    else
                    {
                        wasPrevTranslated = false;
                    }
                    result.append(c);
                    resultLength++;
                }
            }
            return resultLength > 0 ? result.toString() : input;
        }
    });

1

Tôi không muốn gây rối với một số chiến lược đặt tên tùy chỉnh, cũng như không tạo lại một số trình truy cập.
Càng ít mã, tôi càng hạnh phúc.

Điều này đã thực hiện mẹo cho chúng tôi:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates 
public class MyResponse {

    private String id;
    private @JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
    private @JsonProperty("isDeleted") boolean isDeleted;

}

1

Câu trả lời được chấp nhận sẽ không phù hợp với trường hợp của tôi.

Trong trường hợp của tôi, lớp học không thuộc sở hữu của tôi. Lớp có vấn đề đến từ các phụ thuộc của bên thứ 3, vì vậy tôi không thể chỉ thêm @JsonPropertychú thích trong đó.

Để giải quyết vấn đề này, lấy cảm hứng từ câu trả lời @burak ở trên, tôi đã tạo một tùy chỉnh PropertyNamingStrategynhư sau:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
  @Override
  public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
  {
    if (method.getParameterCount() == 1 &&
            (method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
            method.getName().startsWith("set")) {

      Class<?> containingClass = method.getDeclaringClass();
      String potentialFieldName = "is" + method.getName().substring(3);

      try {
        containingClass.getDeclaredField(potentialFieldName);
        return potentialFieldName;
      } catch (NoSuchFieldException e) {
        // do nothing and fall through
      }
    }

    return super.nameForSetterMethod(config, method, defaultName);
  }

  @Override
  public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
  {
    if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
        && method.getName().startsWith("is")) {

      Class<?> containingClass = method.getDeclaringClass();
      String potentialFieldName = method.getName();

      try {
        containingClass.getDeclaredField(potentialFieldName);
        return potentialFieldName;
      } catch (NoSuchFieldException e) {
        // do nothing and fall through
      }
    }
    return super.nameForGetterMethod(config, method, defaultName);
  }
});

Về cơ bản điều này làm là, trước khi tuần tự hóa và giải mã hóa, nó sẽ kiểm tra trong lớp đích / nguồn tên thuộc tính nào hiện diện trong lớp, cho dù đó là thuộc tính isEnabledhay thuộc enabledtính.

Dựa vào đó, trình ánh xạ sẽ tuần tự hóa và giải mã hóa thành tên thuộc tính đang tồn tại.


0

Bạn có thể thay đổi boolean nguyên thủy thành java.lang.Boolean (+ sử dụng @JsonPropery)

@JsonProperty("isA")
private Boolean isA = false;

public Boolean getA() {
    return this.isA;
}

public void setA(Boolean a) {
    this.isA = a;
}

Làm việc xuất sắc cho tôi.

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.