Salesforceからfreee APIを使ってみました

#Apexでfreee APIを使ってJsonを取得した時にオブジェクトとして扱う方法

[JSON.deserializeStrict](https://developer.salesforce.com/docs/atlas.ja-jp.224.0.apexcode.meta/apexcode/apex_class_System_Json.htm#apex_System_Json_deserializeStrict)を使って
JSON 文字列を Apex オブジェクトの指定された型に並列化します。

サンプルを見ると

```
public class Car {
    public String make;
    public String year;
}

public void parse() {        
    Car c = (Car)JSON.deserializeStrict(
        '{"make":"SFDC","year":"2020"}',
        Car.class);
    System.assertEquals(c.make, 'SFDC');
    System.assertEquals(c.year, '2020');
}
```

となっているので、freee APIから取得できるJsonをclassで定義できれば問題なさそうですね。
項目の定義はAPIリファレンスを参照して作ります。


#部門の一覧を取得する

実際のコードは以下のように行います。

```
public static Map<String,Object> getSections() {
        Map<String,Object>  RetMap = new Map<String,Object>();
        Http http = new Http();
        String path ='callout:freeeAPI/sections';//指定ログイン情報
        String companyId = getfreeeCompanyId2(); //本番環境のidを取得する
        String parameters = 'company_id=' + companyId + '&';
        HttpRequest req = new HttpRequest();
        req.setEndpoint(path + '?' + parameters);
        req.setMethod('GET');

        HttpResponse res = http.send(req);
        //JsonをObjectに変換する
        sections sections = (sections)JSON.deserializeStrict(res.getBody(),sections.class);//
        List<section> sectionList = (List<section>)sections.sections;
        Map<Integer,String> SectionMap = new Map<Integer,String>();
        for (section s: sectionList) {
            SectionMap.put(s.id,JSON.serialize(s));            
        }
        RetMap.put('SectionMap',SectionMap);//idと部門情報のMapに変換
      
        return RetMap;
     }

    //部門関係
    //1件分の定義
    public class section {
        public Integer id{get;set;}
        public Integer company_id{get;set;}  
        public String name{get;set;}  
        public String long_name{get;set;}    
        public String shortcut1{get;set;}     
        public String shortcut2{get;set;}    
        public Integer indent_count{get;set;}
        public Integer parent_id{get;set;}       
    }

    //複数件の定義(List型)
    public class sections {
        public List<section> sections;
    }

    //登録修正時の返答用
    public class section_get {
        public section section;
    }
```
classの項目に不足があると　JSON.deserializeStrict(res.getBody(),sections.class);のところでエラーになります。

今回はJSON.deserializeStrictを使いましたが、Jsonの中身が複雑でなければ以下のようにしても値を取ることは可能です。
以下の例でもsectionsのList変数としてApexで扱えます。

```
Map<String, Object> mapJson = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
List<Object> lstObjects = (List<Object>)mapJson.get('sections'); 
```

さらに、他のオブジェクトも見てみましょう。JSON.deserializeStrictがいいことが実感できます。

#取引の一覧を取得する

classの定義は以下のようになります。

```
    //取引関係
    public class details {
        public Integer id;
        public Integer account_item_id;
        public Integer tax_id;           //税区分ID（廃止予定）
        public Integer tax_code;
        public Integer item_id;
        public Integer section_id;
        public Integer[] tag_ids;
        public Integer segment_1_tag_id;
        public Integer segment_2_tag_id;
        public Integer segment_3_tag_id;
        public Integer amount;
        public Integer vat;             //消費税額
        public String description;    
        public String entry_side;       //貸借（貸方: credit, 借方: debit）
    }
    
    public class renews {
        public Integer id;
        public String update_date;  
        public String renew_target_id;
        public String renew_target_type;
        public List<details> details; 
    }
    
    public class payments {
        public Integer id;
        public String date_x;               //dateはエラーになる freeeさんdateは予約語なんで避けて。
        public String from_walletable_type;//口座区分
        public Integer from_walletable_id;
        public Integer amount;
    }
    public class user {
        public Integer id;
        public String email;
        public String display_name;
    }
    
    public class receipts {
		public Integer id;
		public String status;
		public String description;
		public String mime_type;  
        public String issue_date;  
        public String origin;
        public String created_at;
        public String file_src;
        public user user;
    }
    
    public class meta {
        public Integer total_count;
    }

    //****** 取引
    public class deal {
        public Integer id;
        public Integer company_id;    
        public String issue_date;  
        public String due_date;  
        public Integer amount;
        public Integer due_amount;
        public String type;    
        public Integer partner_id;     
        public String partner_code;    
        public String ref_number;
        public String status;
        public List<details> details;  
        public renews renews;
        public List<payments> payments;
        public List<receipts> receipts;        
    }    
    
　　//一覧検索ではこの形で戻ってくる
    public class deals {
        public List<deal> deals;
        public meta meta;
    }
    
    //更新した結果の戻り用
    public class deal_get {
        public deal deal;
    }
```

先ほどの部門に比べて一気に複雑になります。
実際のfreee APIから返ってくるJsonに対応するクラスは**deals**になります。

**deal**クラスのList型変数（複数件）と取得したレコードが何件あるかを示す**meta**クラスからなっています。
また、**deal**クラス自体も**details**クラス、**payments**クラス、**receipts**クラスを
複数件保持する構造になっています。
従って、JSON.deserializeUntypedで並列化する場合は、
この構造に従って、**deal**クラスの中から**details**クラスを取り出すような実装が必要です。
かなり面倒なことになります。

それだけなら、力技でコーディングすればいいのですが、制限もあります。
ヒープエラーで処理ができない場合がありますね。

この時もヒープエラーでしたが、JSON.deserializeStrictで解決できました。
クラスの定義は面倒ですが、後でJsonの仕様変更があっても対応は楽ではと思います。

[how get json response into apex and access fields](https://trailblazers.salesforce.com/answers?feedtype=RECENT&criteria=BESTANSWERS#!/feedtype=SINGLE_QUESTION_DETAIL&dc=All&criteria=ALLQUESTIONS&id=9063A000000eeJQQAY)

#paymentsクラスにあるdate_xに注意して下さい。

この**date_x**はfreeeさんの定義では**date**です。残念ながらApexでは予約語なのでそのままではエラーになります。
仕方ないので**date_x**と定義しておきます。


実際に使う場合には、以下のようにします。

```
       //freeeの中で予約語のdateを項目名にしているので、強制的に変換する。
        deals deals = (deals)JSON.deserializeStrict(resBody.replaceAll(',"date":"',',"date_x":"'),deals.class);
        List<deal> dealsList = (List<deal>)deals.deals;
```

[Salesforceからfreee APIを使ってみました2](https://qiita.com/Keiji_otsubo/items/d53c7d6c72035efb6167)


#残念なことに　2020/07/15困った

久しぶりに取引のAPIをSalesforceからコールしてみました。
何と、エラーですねぇ。

```
13:50:24:000 FATAL_ERROR Class.System.JSON.deserializeStrict: line 19, column 1
13:50:24:000 FATAL_ERROR Class.fkd_freee.Deals1: line 576, column 1
13:50:24:296 FATAL_ERROR System.JSONException: Expected fkd_freee.renews but found [line:1, column:686]
```


ちょうど並列化しているところ。もしやJsonのフォーマットが変わったのでは？

```
//freeeの中で予約語のdateを項目名にしているので、強制的に変換する。
deals deals = (deals)JSON.deserializeStrict(resBody.replaceAll(',"date":"',',"date_x":"'),deals.class);//
```

やっぱり、作った時には無かった(?) renewsの中にdetailsがあるのだけど、レファレンスを見るとフォーマットが違いますね。
困りました。同じdetailsなのに項目が違うclassはSalesforceでは定義できません。
classの定義ができないとJSON.deserializeStrictが使えません。

強制的に変換しようにもちょっと思いつきませんね。
renewsの中にあるdetailsだけを変換する方法は、ないかなぁ。