JSONを抽象型として逆シリアル化できません

Aug 19 2020

JSON文字列を逆シリアル化するテストを実行すると、次のエラーメッセージが表示されます。

JSONを抽象型として逆シリアル化できません:TestController.Item

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

このコードをコンパイルして実行する場合でも、問題がありました。JSONパーサーは実際に使用するサブタイプを認識しないため、Itemのリスト内でSomeItemを実際にインスタンス化することはできませんでした。

そのため、このタイプのコードでの異常な動作を修正するため、クリティカルアップデートは実際には良いことです。解決策は、メモリ内にオブジェクトを手動で作成することです。

これは、JSONを使用するある種のAPIで使用することを意図している場合、設計上の欠陥の可能性も示しています。システムがデータ型に使用するサブクラスの正しい型を決定できないため、それは単に機能しません。Apexのような強く型付けされた言語では、問題があります。

すべてがTestVisibleとマークされているため、正しい解決策はオブジェクトを手動で作成することです。

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

要約すると、直接的な回避策はありません。具体的なクラスに逆シリアル化できる必要があります。できない場合、オプションは、手動で作成する(上記)、JSONGeneratorを使用する、または汎用オブジェクトを使用する(例List<Map<String, Object>>)などです。

2 RobAlexander Aug 22 2020 at 04:40

重要な更新がアクティブ化されたため、今日この問題が発生しました。抽象クラスを仮想クラスに変更することで解決できました。