Jackson เปลี่ยนชื่อฟิลด์บูลีนดั้งเดิมโดยการลบ 'is'


94

สิ่งนี้อาจซ้ำกัน แต่ฉันไม่สามารถหาวิธีแก้ไขปัญหาของฉันได้

ฉันมีชั้นเรียน

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

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

Getters และ setters ถูกสร้างขึ้นโดย Eclipse

ในคลาสอื่นฉันตั้งค่าเป็นจริงและเขียนเป็นสตริง JSON

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

ใน JSON คีย์จะมาเป็น{"success": true}ไฟล์.

ฉันต้องการกุญแจเหมือนisSuccessตัวมันเอง แจ็คสันใช้วิธีเซ็ตเตอร์ในขณะที่อนุกรมหรือไม่ จะทำให้คีย์เป็นชื่อฟิลด์เองได้อย่างไร?


1
ถ้าชื่อคุณสมบัติของคุณคือชื่อisSuccessเมธอดคุณต้องเป็นisIsSuccessฉันคิดว่า
Jens

ฉันเข้าใจ. ฉันคิดว่ามันดีกว่าSetSuccess เมื่อสร้างโดย Eclipse (ตามมาตรฐาน)
iCode

คำตอบ:


120

นี่เป็นคำตอบที่ล่าช้าเล็กน้อย แต่อาจเป็นประโยชน์สำหรับคนอื่น ๆ ที่เข้ามาในหน้านี้

วิธีง่ายๆในการเปลี่ยนชื่อที่ Jackson จะใช้เมื่อทำให้อนุกรมกับ JSON คือการใช้คำอธิบายประกอบ@JsonPropertyดังนั้นตัวอย่างของคุณจะกลายเป็น:

public class MyResponse implements Serializable {

    private boolean isSuccess;

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

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

จากนั้นจะถูกทำให้เป็นอนุกรมเป็น JSON {"isSuccess":true}แต่มีข้อดีคือไม่ต้องแก้ไขชื่อเมธอด getter

โปรดทราบว่าในกรณีนี้คุณสามารถเขียนคำอธิบายประกอบได้เนื่องจาก@JsonProperty("isSuccess")มีเพียงvalueองค์ประกอบเดียว


วิธีนี้จะใช้ไม่ได้กับกรณีของฉันเนื่องจากชั้นเรียนไม่ได้เป็นของฉันเนื่องจากมาจากการอ้างอิงของบุคคลที่สาม สำหรับกรณีดังกล่าวโปรดดูคำตอบของฉันด้านล่าง
edmundpie

5
ฉันใช้สปริงบูตกับแจ็คสัน แต่การได้รับสองฟิลด์หนึ่งคือ "สำเร็จ" และอีกฟิลด์หนึ่งคือ "isSuccess" และเมื่อฉันใช้บูลีนที่ไม่ใช่แบบดั้งเดิมมากกว่าฟิลด์เดียว "isSuccess"
Vishal Singla

@VishalSingla ฉันมีปัญหาเดียวกันแน่นอนโซลูชันนี้สร้างสองฟิลด์ใน Spring Boot
Aron Fiechter

22

ฉันเพิ่งพบปัญหานี้และนี่คือสิ่งที่ฉันพบ แจ็คสันจะตรวจสอบคลาสใด ๆ ที่คุณส่งผ่านไปเพื่อรับและเซ็ตเทอร์และใช้วิธีการเหล่านั้นสำหรับการทำให้เป็นอนุกรมและการแยกส่วน สิ่งที่ตามหลัง "get", "is" และ "set" ในวิธีการเหล่านั้นจะถูกใช้เป็นคีย์สำหรับช่อง JSON ("isValid" สำหรับ getIsValid และ setIsValid)

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

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

ในทำนองเดียวกัน "isSuccess" จะกลายเป็น "ความสำเร็จ" เว้นแต่จะเปลี่ยนชื่อเป็น "isIsSuccess" หรือ "getIsSuccess"

อ่านเพิ่มเติมที่นี่: http://www.citrine.io/blog/2015/5/20/jackson-json-processor


6
isValid ไม่ใช่รูปแบบการตั้งชื่อที่ถูกต้องสำหรับชนิดข้อมูลบูลีนใน java ควรจะถูกต้องและ isValid (), setValid ()
vels4j

2
แต่มันควรจะเป็นอย่างนั้นไม่ใช่เหรอ? อนุสัญญา? หากมีอยู่คุณสามารถเชื่อมโยงไปยังการอ้างอิง Jackson ที่ระบุว่าใช้ชื่อ getter เป็นฟิลด์ JSON ได้หรือไม่ หรือคุณคิดว่าเป็นตัวเลือกการออกแบบที่ไม่ดี?
Abhinav Vishak

2
ฉันหวังว่าจะมีคำเตือนสำหรับสิ่งนี้
RyPope

@ vels4j หลักการตั้งชื่อออกไปนอกหน้าต่างเมื่อคุณกำลังจัดการกับการใช้งานที่เฉพาะเจาะจงสูง
Dragas

13

การใช้คำอธิบายประกอบทั้งสองด้านล่างบังคับให้เอาต์พุต JSON รวมis_xxx:

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

นี่คือคำตอบที่ดีที่สุดสำหรับคำถามนี้
dustinevan

1
นั่นคือ Java? อาจจะเป็น Kotlin?
spottedmahn

5

คุณสามารถกำหนดค่าของคุณได้ObjectMapperดังนี้:

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
ฉันชอบที่คุณกำลังพยายามแก้ปัญหานี้ผ่านการกำหนดค่า แต่นี้จะทำงานถ้าคุณเสมอคำนำหน้าเขตบูลของคุณและ JSON คุณสมบัติด้วย "คือ" สมมติว่าคุณมีฟิลด์บูลีนอีกช่องชื่อ "เปิดใช้งาน" ซึ่งคุณต้องการทำให้เป็นอนุกรม เนื่องจากเมธอดที่สร้างขึ้นคือ "isEnabled ()" โค้ดด้านบนจะทำให้เป็นซีเรียลไลซ์เป็น "isEnabled" แทนที่จะเป็นเพียง "เปิดใช้งาน" ในที่สุดปัญหาก็คือทั้งสองช่อง "x" และ "isX" Eclipse จะสร้างวิธีการ "isX ()" ดังนั้นคุณจึงไม่สามารถอนุมานชื่อคุณสมบัติที่ตรงกับฟิลด์ได้
David Siegal

@DavidSiegal อ้างอิงจากคำตอบของ burak ฉันได้ขยายคำตอบด้านล่างเพื่อสนับสนุนกรณีดังกล่าว
edmundpie

4

เมื่อคุณใช้ Kotlin และคลาสข้อมูล:

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

คุณอาจต้องเพิ่ม@param:JsonProperty("isSuccess")หากคุณกำลังจะยกเลิกการกำหนดค่า JSON ด้วย


2

จากคำตอบของ Utkarsh ..

ชื่อ Getter ลบget /ใช้เป็นชื่อ JSON

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

ถูกจัดเก็บเป็น{"harryPotter": "anythingYouGaveHere"}


สำหรับ Deserialization Jackson จะตรวจสอบทั้งตัวตั้งค่าและชื่อฟิลด์ สำหรับ Json String {"word1": "example"}ทั้งสองรายการด้านล่างนี้ใช้ได้

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

คำถามที่น่าสนใจกว่าคือคำสั่งใดที่แจ็คสันพิจารณาถึงการแยกสายพันธุ์ ถ้าฉันพยายามยกเลิกการกำหนดค่ากลาง {"word1": "myName"}ด้วย

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

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

ฉันไม่ได้ทดสอบกรณีข้างต้น แต่มันน่าสนใจที่จะเห็นค่าของword1 & word2 ...

หมายเหตุ: ฉันใช้ชื่อที่แตกต่างกันอย่างมากเพื่อเน้นว่าฟิลด์ใดที่ต้องเหมือนกัน


1

มีวิธีอื่นสำหรับปัญหานี้

เพียงกำหนดคลาสย่อยใหม่ขยาย PropertyNamingStrategy และส่งต่อไปยังอินสแตนซ์ ObjectMapper

นี่คือตัวอย่างโค้ดอาจช่วยได้มากขึ้น:

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

ฉันไม่ต้องการยุ่งกับกลยุทธ์การตั้งชื่อที่กำหนดเองหรือสร้างตัวเข้าถึงใหม่
ยิ่งรหัสน้อยฉันก็ยิ่งมีความสุข

นี่เป็นเคล็ดลับสำหรับเรา:

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

คำตอบที่ยอมรับจะใช้ไม่ได้กับกรณีของฉัน

ในกรณีของฉันชั้นเรียนไม่ได้เป็นของฉัน คลาสที่มีปัญหามาจากการอ้างอิงของบุคคลที่สามดังนั้นฉันจึงไม่สามารถเพิ่ม@JsonPropertyคำอธิบายประกอบในนั้นได้

เพื่อแก้ปัญหานี้ได้รับแรงบันดาลใจจากคำตอบของ @burak ด้านบนฉันสร้างแบบกำหนดเองPropertyNamingStrategyดังนี้:

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

โดยพื้นฐานแล้วสิ่งนี้คืออะไรก่อนที่จะทำให้เป็นอนุกรมและ deserializing จะตรวจสอบในคลาสเป้าหมาย / ซอร์สซึ่งมีชื่อคุณสมบัติอยู่ในคลาสไม่ว่าจะเป็นisEnabledหรือenabledคุณสมบัติ

จากนั้นผู้ทำแผนที่จะทำให้เป็นอนุกรมและดีซีเรียลไลซ์กับชื่อคุณสมบัติที่มีอยู่


0

คุณสามารถเปลี่ยนบูลีนดั้งเดิมเป็น java.lang.Boolean (+ use @JsonPropery)

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

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

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

ทำงานได้ดีสำหรับฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.