Gson: วิธียกเว้นฟิลด์เฉพาะจากการทำให้เป็นอนุกรมโดยไม่มีคำอธิบายประกอบ


413

ฉันพยายามที่จะเรียนรู้ Gson และฉันกำลังดิ้นรนกับการกีดกันภาคสนาม นี่คือชั้นเรียนของฉัน

public class Student {    
  private Long                id;
  private String              firstName        = "Philip";
  private String              middleName       = "J.";
  private String              initials         = "P.F";
  private String              lastName         = "Fry";
  private Country             country;
  private Country             countryOfBirth;
}

public class Country {    
  private Long                id;
  private String              name;
  private Object              other;
}

ฉันสามารถใช้ GsonBuilder และเพิ่ม ExclusionStrategy สำหรับชื่อฟิลด์เหมือนfirstNameหรือแต่ฉันไม่สามารถดูเหมือนจะจัดการเพื่อยกเว้นคุณสมบัติของเขตข้อมูลบางอย่างเช่นcountrycountry.name

การใช้วิธีการpublic boolean shouldSkipField(FieldAttributes fa)นี้ FieldAttributes มีข้อมูลไม่เพียงพอที่จะจับคู่ฟิลด์กับตัวกรองcountry.nameดังนี้

PS: ฉันต้องการหลีกเลี่ยงคำอธิบายประกอบเนื่องจากฉันต้องการปรับปรุงเรื่องนี้และใช้ RegEx เพื่อกรองฟิลด์

แก้ไข : ฉันพยายามดูว่าเป็นไปได้ที่จะเลียนแบบพฤติกรรมของปลั๊กอิน Struts2 JSON

ใช้งาน Gson

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>

แก้ไข: ฉันเปิดคำถามอีกครั้งด้วยการเพิ่มต่อไปนี้:

ฉันได้เพิ่มเขตข้อมูลที่สองที่มีประเภทเดียวกันเพื่ออธิบายปัญหานี้ให้ละเอียดยิ่งขึ้น โดยทั่วไปฉันต้องการยกเว้นแต่ไม่country.name countrOfBirth.nameฉันไม่ต้องการยกเว้นประเทศเป็นประเภท ดังนั้นประเภทจะเหมือนกันคือสถานที่จริงในกราฟวัตถุที่ฉันต้องการระบุและยกเว้น


1
ยังเป็นรุ่น 2.2 ฉันยังคงไม่สามารถระบุเส้นทางไปยังฟิลด์ที่จะยกเว้น flexjson.sourceforge.netรู้สึกเหมือนเป็นทางเลือกที่ดี
Liviu T.

ลองดูคำตอบของฉันสำหรับคำถามที่คล้ายกัน มันขึ้นอยู่กับการสร้างที่กำหนดเองJsonSerializerสำหรับบางประเภท - Countryในกรณีของคุณซึ่งจะถูกนำไปใช้ExclusionStrategyซึ่งจะตัดสินใจว่าฟิลด์ใดที่จะทำให้เป็นอันดับ
pirho

คำตอบ:


625

ฟิลด์ใด ๆ ที่คุณไม่ต้องการให้เป็นอันดับโดยทั่วไปคุณควรใช้ตัวแก้ไข "ชั่วคราว" และสิ่งนี้ยังใช้กับ json serializers

หากคุณไม่ต้องการให้ชื่อปรากฏใน json ต่อเนื่องให้มันเป็นคำหลักชั่วคราวเช่น:

private transient String name;

รายละเอียดเพิ่มเติมในเอกสารประกอบของ Gson


6
มันเกือบจะเหมือนกันกับการเพิ่มความคิดเห็นแบบแยกส่วนเหมือนกับที่ใช้กับอินสแตนซ์ทั้งหมดของคลาสนั้น ฉันต้องการการยกเว้นแบบไดนามิกรันไทม์ ฉันมีบางกรณีที่ฉันต้องการให้มีการยกเว้นบางฟิลด์เพื่อให้การตอบสนองที่เบาลง / จำกัด และในบางกรณีฉันต้องการวัตถุเต็มรูปแบบต่อเนื่อง
Liviu T.

34
สิ่งหนึ่งที่ควรทราบก็คือผลกระทบชั่วคราวทั้งต่อเนื่องและการดีซีเรียลไลเซชัน นอกจากนี้ยังจะปล่อยค่าจากการเป็นอนุกรมลงในวัตถุแม้ว่าจะมีอยู่ใน JSON

3
ปัญหาของการใช้transientแทน@Exposeคือคุณยังต้องจำลอง POJO บนไคลเอนต์ของคุณด้วยฟิลด์ทั้งหมดที่อาจเข้ามาได้ในกรณีของ back-end API ที่อาจใช้ร่วมกันระหว่างโปรเจ็กต์อาจเป็นปัญหาในกรณี มีการเพิ่มฟิลด์เพิ่มเติม เป็นหลักมันเป็นรายการที่อนุญาตเทียบกับบัญชีดำของเขตข้อมูล
theblang

11
วิธีนี้ไม่ได้ผลสำหรับฉันเพราะมันไม่เพียง แต่จะแยกข้อมูลออกจากการทำให้เป็นอนุกรมของ gson แต่ยังไม่รวมข้อมูลนั้นจากการทำให้เป็นอันดับภายในแอป (โดยใช้อินเตอร์เฟซแบบอนุกรม)
pkk

8
ชั่วคราวป้องกันการ SERIALIZATION และการกำหนดฟิลด์ สิ่งนี้ไม่ตรงกับความต้องการของฉัน
Loenix

318

Nishant เป็นทางออกที่ดี แต่มีวิธีที่ง่ายกว่า เพียงทำเครื่องหมายฟิลด์ที่ต้องการด้วยคำอธิบายประกอบ @Expose เช่น:

@Expose private Long id;

ปล่อยให้ฟิลด์ใด ๆ ที่คุณไม่ต้องการทำให้เป็นอันดับ จากนั้นเพียงสร้างวัตถุ Gson ของคุณด้วยวิธีนี้:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

95
เป็นไปได้หรือไม่ที่จะมีบางอย่างเช่น "notExpose" และละเว้นเฉพาะกรณีที่มีเพียงฟิลด์เดียวเท่านั้นที่ต้องต่อเนื่องกันและการเพิ่มคำอธิบายประกอบให้กับฟิลด์ทั้งหมดนั้นซ้ำซ้อน
Daniil Shevelev

2
@DaSh ฉันเพิ่งมีสถานการณ์ดังกล่าว มันง่ายมากในการเขียน ExclusionStrategy ที่กำหนดเองซึ่งทำเช่นนั้น ดูคำตอบของ Nishant ปัญหาเดียวคือการรวมกลุ่มของภาชนะคลาสและซอกับ skipclass vs skipfield (เขตข้อมูลสามารถเป็นคลาส ... )
keyser

1
@DaSh คำตอบของฉันด้านล่างทำอย่างนั้น
Derek Shockey

ช่างเป็นทางออกที่ดี ฉันทำงานในสถานการณ์ที่ฉันต้องการเขตข้อมูลเป็นดิสก์ แต่จะถูกละเว้นเมื่อส่งไปยังเซิร์ฟเวอร์ผ่าน gson สมบูรณ์แบบขอบคุณ!
Slynk

1
@Danlil คุณควรจะสามารถใช้ @Expose (ซีเรียลไลซ์ = false, ดีซีเรียลไลซ์ = false)
Hrk

237

ดังนั้นคุณต้องการที่จะไม่รวม และfirstName country.nameนี่คือสิ่งที่คุณExclusionStrategyควรมีลักษณะเหมือน

    public class TestExclStrat implements ExclusionStrategy {

        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
            (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
        }

    }

ถ้าคุณเห็นอย่างใกล้ชิดก็จะส่งกลับtrueสำหรับStudent.firstNameและCountry.nameซึ่งเป็นสิ่งที่คุณต้องการยกเว้น

คุณต้องสมัครExclusionStrategyแบบนี้

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat())
        //.serializeNulls() <-- uncomment to serialize NULL fields as well
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json);

ผลตอบแทนนี้:

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

ฉันคิดว่าวัตถุของประเทศนั้นเริ่มต้นได้id = 91Lในชั้นเรียนของนักเรียน


คุณอาจได้รับแฟนซี ตัวอย่างเช่นคุณไม่ต้องการซีเรียลไลซ์ฟิลด์ใด ๆ ที่มีสตริง "name" ในชื่อ ทำเช่นนี้:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name"); 
}

สิ่งนี้จะส่งคืน:

{ "initials": "P.F", "country": { "id": 91 }}

แก้ไข:เพิ่มข้อมูลเพิ่มเติมตามที่ร้องขอ

สิ่งนี้ExclusionStrategyจะทำสิ่งนี้ แต่คุณต้องผ่าน "ชื่อฟิลด์ที่ผ่านการรับรองโดยสมบูรณ์" ดูด้านล่าง:

    public class TestExclStrat implements ExclusionStrategy {

        private Class<?> c;
        private String fieldName;
        public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
        {
            this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
            this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
        }
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
        }

    }

นี่คือวิธีที่เราสามารถใช้มันโดยทั่วไป

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
        //.serializeNulls()
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json); 

มันกลับมา:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}

ขอบคุณสำหรับคำตอบ สิ่งที่ฉันต้องการคือการสร้าง ExclusionStrategy ที่สามารถใช้สตริงเหมือนcountry.nameและมีเพียง exlude สนามnameเมื่อ serializing countryสนาม มันควรจะเป็นทั่วไปพอที่จะนำไปใช้กับทุกชั้นที่มีคุณสมบัติชื่อประเทศของระดับประเทศ ฉันไม่ต้องการสร้าง ExclusionStrategy สำหรับทุกชั้นเรียน
Liviu T.

@Liviu T. ฉันได้อัปเดตคำตอบแล้ว ที่ใช้วิธีการทั่วไป คุณอาจได้รับความคิดสร้างสรรค์มากขึ้น แต่ฉันเก็บไว้เป็นองค์ประกอบ
Nishant

Ty สำหรับการอัพเดท What I; m พยายามดูว่าเป็นไปได้หรือไม่ที่จะทราบว่าในกราฟวัตถุฉันเป็นวิธีที่เรียกว่าดังนั้นฉันสามารถยกเว้นบางเขตของประเทศ แต่ไม่ใช่ countryOfBirth (ตัวอย่าง) คลาสเดียวกัน แต่มีคุณสมบัติที่แตกต่างกัน ฉันได้แก้ไขคำถามของฉันเพื่อชี้แจงสิ่งที่ฉันพยายามจะบรรลุ
Liviu T.

มีวิธียกเว้นฟิลด์ที่มีค่าว่างหรือไม่?
Yusuf K.

12
ควรทำเครื่องหมายคำตอบนี้เป็นคำตอบที่ต้องการ แตกต่างจากคำตอบอื่น ๆ ที่มีคะแนนเสียงมากกว่าในปัจจุบันวิธีนี้ไม่ต้องการให้คุณแก้ไขคลาส bean นี่เป็นข้อดีอย่างมาก จะเกิดอะไรขึ้นถ้ามีคนอื่นกำลังใช้คลาส bean เดียวกันและคุณทำเครื่องหมายฟิลด์ที่พวกเขาต้องการยืนยันว่าเป็น "ชั่วคราว"
user64141

221

หลังจากอ่านคำตอบทั้งหมดที่ฉันพบพบว่าในกรณีของฉันมีความยืดหยุ่นมากที่สุดคือการใช้@Excludeคำอธิบายประกอบที่กำหนดเอง ดังนั้นฉันจึงใช้กลยุทธ์ง่าย ๆ สำหรับเรื่องนี้ (ฉันไม่ต้องการทำเครื่องหมายทุกฟิลด์โดยใช้@Exposeหรือไม่ต้องการใช้transientซึ่งขัดแย้งกับในการSerializableทำให้เป็นอันดับแอป):

คำอธิบายประกอบ:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

กลยุทธ์:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

การใช้งาน:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();

16
ในฐานะที่เป็นหมายเหตุเพิ่มเติมหากคุณต้องการใช้กลยุทธ์การแยกของคุณสำหรับการทำให้เป็นอนุกรมเท่านั้นหรือการดีซีเรียลaddSerializationExclusionStrategyaddDeserializationExclusionStrategysetExclusionStrategies
ไลซ์เซชั่น

9
ที่สมบูรณ์แบบ! โซลูชันชั่วคราวไม่ทำงานสำหรับฉันเพราะฉันใช้ Realm สำหรับ DB และฉันต้องการยกเว้นฟิลด์จาก Gson เท่านั้น แต่ไม่ใช่ Realm (ซึ่ง transient ชั่วคราวทำ)
Marcio Granzotto

3
นี่ควรเป็นคำตอบที่ยอมรับได้ หากต้องการละเว้นช่องว่างf.getAnnotation(Exclude.class) != nullให้เปลี่ยนเป็นf.getAnnotation(Exclude.class) == null
Sharp Edge

3
ยอดเยี่ยมเมื่อคุณไม่สามารถใช้งานได้transientเนื่องจากความต้องการของห้องสมุดอื่น ขอบคุณ!
Martin D

1
ยังยอดเยี่ยมสำหรับฉันเพราะ Android ทำให้คลาสของฉันอยู่ระหว่างกิจกรรมต่าง ๆ แต่ฉันต้องการให้พวกเขายกเว้นเมื่อฉันใช้ GSON เท่านั้น แอปนี้ให้แอปของฉันทำงานต่อไปเหมือนเดิมจนกระทั่งมันต้องการปิดมันเพื่อส่งไปให้ผู้อื่น
ThePartyTurtle

81

ฉันพบปัญหานี้ซึ่งฉันมีฟิลด์จำนวนน้อยที่ฉันต้องการแยกออกจากการทำให้เป็นอนุกรมเท่านั้นดังนั้นฉันจึงพัฒนาโซลูชันที่ค่อนข้างง่ายซึ่งใช้การเพิ่มความคิดเห็นของ Gson @Exposeด้วยกลยุทธ์การยกเว้นแบบกำหนดเอง

วิธีการใช้งานแบบบิวด์อินเท่านั้น@Exposeโดยการตั้งค่าGsonBuilder.excludeFieldsWithoutExposeAnnotation()แต่ตามชื่อบ่งชี้ฟิลด์ที่ไม่มีการแจ่มชัด@Exposeจะถูกละเว้น เนื่องจากฉันมีเพียงไม่กี่ฟิลด์ที่ฉันต้องการยกเว้นฉันพบว่าโอกาสที่จะเพิ่มคำอธิบายประกอบให้กับทุกฟิลด์นั้นยุ่งยากมาก

ฉันต้องการการผกผันอย่างมีประสิทธิภาพซึ่งทุกอย่างถูกรวมไว้เว้นแต่ฉัน@Exposeจะใช้อย่างชัดเจนเพื่อแยกออก ฉันใช้กลยุทธ์การยกเว้นต่อไปนี้เพื่อทำสิ่งนี้:

new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.serialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .addDeserializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.deserialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .create();

ตอนนี้ฉันสามารถยกเว้นบางฟิลด์ด้วย@Expose(serialize = false)หรือใส่@Expose(deserialize = false)คำอธิบายประกอบได้อย่างง่ายดาย(โปรดทราบว่าค่าเริ่มต้นสำหรับ@Exposeแอตทริบิวต์ทั้งสองคือtrue) คุณสามารถใช้งานได้แน่นอน@Expose(serialize = false, deserialize = false)แต่สามารถทำได้อย่างกระชับโดยการประกาศฟิลด์transientแทน (ซึ่งยังคงมีผลกับกลยุทธ์การยกเว้นแบบกำหนดเองเหล่านี้)


เพื่อประสิทธิภาพฉันสามารถดูกรณีสำหรับการใช้ @Expose (serialize = false, deserialize = false) แทนที่จะเป็นแบบชั่วคราว
paiego

1
@ paiego คุณสามารถขยายได้ไหม ตอนนี้ฉันถูกลบออกจากการใช้ Gson หลายปีและฉันไม่เข้าใจว่าเพราะเหตุใดคำอธิบายประกอบจึงมีประสิทธิภาพมากกว่าการทำเครื่องหมายชั่วคราว
Derek Shockey

อ่าฉันทำผิดพลาดไปแล้วขอบคุณที่รับสิ่งนี้ ฉันเข้าใจผิดว่าสารระเหยสำหรับชั่วคราว (เช่นไม่มีการแคชดังนั้นจึงไม่มีปัญหาการเชื่อมโยงกันของแคชกับความผันผวน แต่มีประสิทธิภาพน้อยกว่า) อย่างไรก็ตามรหัสของคุณใช้งานได้ดี!
paiego

18

คุณสามารถสำรวจต้นไม้ json ด้วย gson

ลองสิ่งนี้:

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");

คุณสามารถเพิ่มคุณสมบัติบางอย่างเช่น:

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);

ทดสอบกับ gson 2.2.4


3
ฉันสงสัยว่านี่เป็นการแสดงที่มีประสิทธิภาพมากเกินไปหรือไม่หากคุณต้องการกำจัดคุณสมบัติที่ซับซ้อนซึ่งต้องมีการแยกวิเคราะห์ก่อนที่จะนำออก คิด?
เบ็น

ไม่ใช่วิธีแก้ปัญหาที่ปรับขนาดได้อย่างแน่นอนลองจินตนาการถึงอาการปวดหัวทั้งหมดที่คุณจะต้องทำหากคุณเปลี่ยนโครงสร้างของวัตถุหรือเพิ่ม / ลบสิ่งต่าง ๆ
codenamezero

16

ฉันมากับโรงงานคลาสเพื่อรองรับฟังก์ชั่นนี้ ผ่านการรวมกันของฟิลด์หรือคลาสใด ๆ ที่คุณต้องการยกเว้น

public class GsonFactory {

    public static Gson build(final List<String> fieldExclusions, final List<Class<?>> classExclusions) {
        GsonBuilder b = new GsonBuilder();
        b.addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return fieldExclusions == null ? false : fieldExclusions.contains(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return classExclusions == null ? false : classExclusions.contains(clazz);
            }
        });
        return b.create();

    }
}

หากต้องการใช้ให้สร้างสองรายการ (แต่ละรายการเป็นทางเลือก) และสร้างวัตถุ GSON ของคุณ:

static {
 List<String> fieldExclusions = new ArrayList<String>();
 fieldExclusions.add("id");
 fieldExclusions.add("provider");
 fieldExclusions.add("products");

 List<Class<?>> classExclusions = new ArrayList<Class<?>>();
 classExclusions.add(Product.class);
 GSON = GsonFactory.build(null, classExclusions);
}

private static final Gson GSON;

public String getSomeJson(){
    List<Provider> list = getEntitiesFromDatabase();
    return GSON.toJson(list);
}

แน่นอนว่าสิ่งนี้สามารถแก้ไขได้เพื่อดูชื่อที่มีคุณสมบัติครบถ้วนและแยกออกจากการแข่งขัน ...
Domenic D.

ฉันทำตัวอย่างด้านล่าง สิ่งนี้ไม่ทำงาน กรุณาแนะนำส่วนตัวคง Gson GSON สุดท้าย คง {รายการ <String> fieldExclusions = ใหม่ ArrayList <String> (); fieldExclusions.add ( "id"); GSON = GsonFactory.build (fieldExclusions, null); } สตริงคงที่ส่วนตัว getSomeJson () {String jsonStr = "[{\" id \ ": 111, \" ชื่อ \ ": \" praveen \ ", \" อายุ \ ": 16}, {\" id \ ": 222 \ "ชื่อ \": \ "Prashant \" \ "อายุ \": 20}] "; ส่งคืน jsonStr; } โมฆะคงที่สาธารณะหลัก (String [] args) {String jsonStr = getSomeJson (); System.out.println (GSON.toJson (jsonStr)); }
Praveen Hiremath

13

ฉันแก้ไขปัญหานี้ด้วยคำอธิบายประกอบที่กำหนดเอง นี่คือคลาสคำอธิบายประกอบ "SkipSerialisation" ของฉัน:

@Target (ElementType.FIELD)
public @interface SkipSerialisation {

}

และนี่คือ GsonBuilder ของฉัน:

gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() {

  @Override public boolean shouldSkipField (FieldAttributes f) {

    return f.getAnnotation(SkipSerialisation.class) != null;

  }

  @Override public boolean shouldSkipClass (Class<?> clazz) {

    return false;
  }
});

ตัวอย่าง:

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}

5
Gson: วิธียกเว้นฟิลด์เฉพาะจากการทำให้เป็นอนุกรมโดยไม่มีคำอธิบายประกอบ
Ben

3
คุณควรเพิ่ม@Retention(RetentionPolicy.RUNTIME)คำอธิบายประกอบของคุณ
David Novák

9

หรือสามารถพูดได้ว่าฟิลด์ใดจะไม่เปิดเผยด้วย:

Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();

ในชั้นเรียนของคุณเกี่ยวกับคุณลักษณะ:

private **transient** boolean nameAttribute;

17
เขตข้อมูลชั่วคราวและคงที่จะถูกยกเว้นโดยค่าเริ่มต้น; ไม่จำเป็นต้องเรียกร้องexcludeFieldsWithModifiers()สิ่งนั้น
Derek Shockey

9

ฉันใช้กลยุทธ์นี้: ฉันยกเว้นทุกฟิลด์ที่ไม่ได้ทำเครื่องหมายด้วยคำอธิบายประกอบ@SerializedNameเช่น:

public class Dummy {

    @SerializedName("VisibleValue")
    final String visibleValue;
    final String hiddenValue;

    public Dummy(String visibleValue, String hiddenValue) {
        this.visibleValue = visibleValue;
        this.hiddenValue = hiddenValue;
    }
}


public class SerializedNameOnlyStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(SerializedName.class) == null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}


Gson gson = new GsonBuilder()
                .setExclusionStrategies(new SerializedNameOnlyStrategy())
                .create();

Dummy dummy = new Dummy("I will see this","I will not see this");
String json = gson.toJson(dummy);

มันกลับมา

{"VisibleValue": "ฉันจะเห็นสิ่งนี้"}


6

วิธีการอื่น (มีประโยชน์โดยเฉพาะอย่างยิ่งถ้าคุณจำเป็นต้องตัดสินใจที่จะยกเว้นเขตข้อมูลที่รันไทม์) คือการลงทะเบียน TypeAdapter กับอินสแตนซ์ gson ของคุณ ตัวอย่างด้านล่าง:

Gson gson = new GsonBuilder()
.registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer())

ในกรณีด้านล่างเซิร์ฟเวอร์จะคาดหวังหนึ่งในสองค่า แต่เนื่องจากพวกเขาทั้งคู่ ints แล้ว gson จะทำให้พวกเขาทั้งสองเป็นอันดับ เป้าหมายของฉันคือละเว้นค่าใด ๆ ที่เป็นศูนย์ (หรือน้อยกว่า) จาก json ที่โพสต์ไปยังเซิร์ฟเวอร์

public class BloodPressurePostSerializer implements JsonSerializer<BloodPressurePost> {

    @Override
    public JsonElement serialize(BloodPressurePost src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();

        if (src.systolic > 0) {
            jsonObject.addProperty("systolic", src.systolic);
        }

        if (src.diastolic > 0) {
            jsonObject.addProperty("diastolic", src.diastolic);
        }

        jsonObject.addProperty("units", src.units);

        return jsonObject;
    }
}

6

@Transientคำอธิบายประกอบของ Kotlin ยังทำเคล็ดลับที่เห็นได้ชัด

data class Json(
    @field:SerializedName("serialized_field_1") val field1: String,
    @field:SerializedName("serialized_field_2") val field2: String,
    @Transient val field3: String
)

เอาท์พุท:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}

1

ฉันทำงานเพียงแค่ใส่@Exposeคำอธิบายประกอบนี่คือรุ่นที่ฉันใช้

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

ในModelชั้นเรียน:

@Expose
int number;

public class AdapterRestApi {

ในAdapterชั้นเรียน:

public EndPointsApi connectRestApi() {
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(90000, TimeUnit.SECONDS)
            .readTimeout(90000,TimeUnit.SECONDS).build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ConstantRestApi.ROOT_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();

    return retrofit.create  (EndPointsApi.class);
}

1

ฉันมีรุ่น Kotlin

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
internal annotation class JsonSkip

class SkipFieldsStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes): Boolean {
        return f.getAnnotation(JsonSkip::class.java) != null
    }
}

และวิธีที่คุณสามารถเพิ่มสิ่งนี้ลงใน Retrofit GSONConverterFactory:

val gson = GsonBuilder()
                .setExclusionStrategies(SkipFieldsStrategy())
                //.serializeNulls()
                //.setDateFormat(DateFormat.LONG)
                //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                //.setPrettyPrinting()
                //.registerTypeAdapter(Id.class, IdTypeAdapter())
                .create()
        return GsonConverterFactory.create(gson)

0

นี่คือสิ่งที่ฉันมักจะใช้:

พฤติกรรมเริ่มต้นที่นำมาใช้ใน Gson คือฟิลด์วัตถุ null จะถูกละเว้น

หมายถึงวัตถุ Gson ไม่ทำให้เป็นอันดับเขตข้อมูลที่มีค่าเป็นศูนย์ถึง JSON ถ้าเขตข้อมูลในวัตถุ Java เป็นโมฆะ Gson จะแยกออก

คุณสามารถใช้ฟังก์ชั่นนี้เพื่อแปลงวัตถุให้เป็นโมฆะหรือตั้งค่าได้เอง

     /**
   * convert object to json
   */
  public String toJson(Object obj) {
    // Convert emtpy string and objects to null so we don't serialze them
    setEmtpyStringsAndObjectsToNull(obj);
    return gson.toJson(obj);
  }

  /**
   * Sets all empty strings and objects (all fields null) including sets to null.
   *
   * @param obj any object
   */
  public void setEmtpyStringsAndObjectsToNull(Object obj) {
    for (Field field : obj.getClass().getDeclaredFields()) {
      field.setAccessible(true);
      try {
        Object fieldObj = field.get(obj);
        if (fieldObj != null) {
          Class fieldType = field.getType();
          if (fieldType.isAssignableFrom(String.class)) {
            if(fieldObj.equals("")) {
              field.set(obj, null);
            }
          } else if (fieldType.isAssignableFrom(Set.class)) {
            for (Object item : (Set) fieldObj) {
              setEmtpyStringsAndObjectsToNull(item);
            }
            boolean setFielToNull = true;
            for (Object item : (Set) field.get(obj)) {
              if(item != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          } else if (!isPrimitiveOrWrapper(fieldType)) {
            setEmtpyStringsAndObjectsToNull(fieldObj);
            boolean setFielToNull = true;
            for (Field f : fieldObj.getClass().getDeclaredFields()) {
              f.setAccessible(true);
              if(f.get(fieldObj) != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          }
        }
      } catch (IllegalAccessException e) {
        System.err.println("Error while setting empty string or object to null: " + e.getMessage());
      }
    }
  }

  private void setFieldToNull(Object obj, Field field) throws IllegalAccessException {
    if(!Modifier.isFinal(field.getModifiers())) {
      field.set(obj, null);
    }
  }

  private boolean isPrimitiveOrWrapper(Class fieldType)  {
    return fieldType.isPrimitive()
        || fieldType.isAssignableFrom(Integer.class)
        || fieldType.isAssignableFrom(Boolean.class)
        || fieldType.isAssignableFrom(Byte.class)
        || fieldType.isAssignableFrom(Character.class)
        || fieldType.isAssignableFrom(Float.class)
        || fieldType.isAssignableFrom(Long.class)
        || fieldType.isAssignableFrom(Double.class)
        || fieldType.isAssignableFrom(Short.class);
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.