ไม่สามารถ deserialize JSON เป็นชนิดนามธรรม

Aug 19 2020

เมื่อเรียกใช้การทดสอบที่ deserializing สตริง JSON ฉันได้รับข้อความแสดงข้อผิดพลาด:

ไม่สามารถ deserialize JSON เป็นชนิดนามธรรม: TestController.Iem

JSON มีรายการของวัตถุที่เป็นนามธรรมและไม่สามารถยกเลิกการกำหนดค่าเริ่มต้นได้

public class TestController {
    
    @Testvisible
    private abstract class Item{
        public String bla;
        public String bla2;
    }


    public class SomeItem extends Item{
        private SomeItem() {
            bla = 'bla';
        }
    }


    public class SomeClass{

        @Testvisible private List<Item> items;

        public Graph(List<SomeItem> someItems) {
            items = new List<Item>();
            items.addAll((List<Item>) someItems);
        }
    }
}

นี่คือคลาสทดสอบ:

@IsTest
public class TestController_Test {
    
    @IsTest
    private static void testSomething() {
        TestController.SomeClass someClass = (TestController.SomeClass) System.JSON.deserialize(json, TestController.SomeClass.class);

        for(TestController.Item item : someClass.items) {

        }
    }       
}

ฉันพบคำขอดึง github ที่มีข้อผิดพลาดเดียวกัน มีการอัปเดตที่สำคัญและฉันแน่ใจว่านี่เป็นสาเหตุของปัญหา

คุณมีความคิดที่จะแก้ปัญหานั้นหรือไม่?

คำตอบ

5 sfdcfox Aug 19 2020 at 22:44

แม้ว่าโค้ดนี้จะคอมไพล์และรัน แต่ก็มีปัญหา คุณไม่สามารถสร้างอินสแตนซ์ SomeItem ภายในรายการไอเท็มได้จริงเนื่องจากตัวแยกวิเคราะห์ JSON ไม่ทราบว่าคุณหมายถึงการใช้ประเภทย่อยใด

ด้วยเหตุนี้การอัปเดตที่สำคัญจึงเป็นสิ่งที่ดีเพราะจะแก้ไขพฤติกรรมที่ผิดปกติด้วยรหัสประเภทนี้ วิธีแก้ปัญหาคือการสร้างวัตถุในหน่วยความจำด้วยตนเอง

นอกจากนี้ยังชี้ให้เห็นถึงข้อบกพร่องในการออกแบบที่เป็นไปได้หากต้องการใช้กับ API บางประเภทที่จะใช้ JSON มันใช้งานไม่ได้เพราะระบบไม่สามารถระบุประเภทของคลาสย่อยที่ถูกต้องที่จะใช้สำหรับประเภทข้อมูลได้และในภาษาที่พิมพ์อย่างรุนแรงเช่น Apex นั่นเป็นปัญหา

เนื่องจากทุกอย่างถูกทำเครื่องหมายเป็น TestVisible วิธีแก้ไขที่ถูกต้องคือการสร้างวัตถุด้วยตนเอง

TestController.SomeClass wrapper = new TestController.SomeClass();
wrapper.items = new TestController.SomeItem[0];
wrapper.items.add(new TestController.SomeItem());
// etc //

โดยสรุปไม่มีวิธีแก้ปัญหาโดยตรง คุณต้องสามารถ deserialize เป็นคลาสที่เป็นรูปธรรมได้ หากคุณทำไม่ได้ตัวเลือกของคุณจะเป็นเช่นการสร้างด้วยตนเอง (ด้านบน) โดยใช้ JSONGenerator หรือใช้วัตถุทั่วไป (เช่นList<Map<String, Object>>)

2 RobAlexander Aug 22 2020 at 04:40

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