Diterjemahkan dengan AI. Belum ditinjau oleh komunitas. Baca dalam bahasa Inggris

Blog · 13 September 2023 · Steve Simpson · Integrasi

JSON Jamboree: Adu Deserialisasi Tetap vs. Dinamis untuk Callout Apex

Pendahuluan:

Halo! Saya Steve Simpson, Salesforce Certified Technical Architect dan instruktur. Dalam artikel ini, kita akan menjelajahi berbagai teknik untuk mengurai JSON saat melakukan callout Apex di Salesforce. Kita akan membahas penguraian JSON tetap, penguraian JSON dinamis, dan pendekatan hibrida yang menawarkan fleksibilitas dan kemudahan penggunaan.

Bagian 1: Penguraian JSON Tetap dengan Deserialisasi Satu Baris

Saat menangani struktur JSON sederhana yang jarang berubah, Anda bisa membuat kelas Apex yang persis sesuai dengan struktur JSON-nya. Pendekatan ini memungkinkan Anda memakai satu baris kode untuk mendeserialisasi string JSON menjadi array. Metode ini andal dan efisien untuk struktur JSON yang statis.

Tangkapan layar: dokumentasi API kedatangan penerbangan OpenSky, dengan parameter permintaannya Tangkapan layar Postman: panggilan GET ke API kedatangan OpenSky yang mengembalikan data penerbangan sebagai JSON

public class OS_FlightStatusModel
{
public String icao24 {get;set;}
public Integer firstSeen {get;set;}
public String estDepartureAirport {get;set;}
public Integer lastSeen {get;set;}
public String estArrivalAirport {get;set;}
public String callsign {get;set;}
public Integer estDepartureAirportHorizDistance {get;set;}
public Integer estDepartureAirportVertDistance {get;set;}
public Integer estArrivalAirportHorizDistance {get;set;}
public Integer estArrivalAirportVertDistance {get;set;}
public Integer departureAirportCandidatesCount {get;set;}
public Integer arrivalAirportCandidatesCount {get;set;}
//---Use the JSON Deserialize
public static List<OS_FlightStatusModel> parse(String json)
{
return (List<OS_FlightStatusModel>) System.JSON.deserialize(json, List<OS_FlightStatusModel>.class);
}
}

Dengan kelas di atas, Anda bisa dengan mudah mengurai JSON memakai satu baris kode:

//---Get the Flight Request
private static List<OS_FlightStatusModel> getFlightRequest(String requestURL)
{
//---Return structure
List<OS_FlightStatusModel> retList = new List<OS_FlightStatusModel>();
//---Make the request
HttpResponse resp = getRequest( requestURL);
//---Check for Success
Integer respStatusCode = resp.getStatusCode();
if (respStatusCode == 200)
{
//---Read the Payload
String payload = resp.getBody();
debug('Payload: ' + payload);
//---Parse the payload
retList = OS_FlightStatusModel.parse(payload);
if (retList != null)
{
System.debug('Found ' + retList.size() + ' records');
for( OS_FlightStatusModel mRow : retList)
{
debug('Row: ' + mRow);
}
}
}
else
{
//---Handle an error
debug('Response Status Code: ' + respStatusCode);
}
return retList;
}

Bagian 2: Penguraian JSON Dinamis

Untuk struktur JSON yang lebih rumit, seperti array atau objek bersarang, Anda mungkin perlu memakai penguraian JSON dinamis. Pendekatan ini membutuhkan lebih banyak baris kode dan navigasi yang cermat melalui token JSON. Meskipun tampak lebih rumit, penguraian JSON dinamis diperlukan saat menangani struktur JSON yang lebih lanjut.

Contoh:

/****************************************************************************************
* @Name OS_StateModelParser
* @Author Steve Simpson - steve@stevetecharc.com
* @Date 2023
*
* @Description Parser for reading State Models from JSON
****************************************************************************************
* Version Developer Date Description
*----------------------------------------------------------------------------------------
* 1.0 Steve Simpson 2023 Initial Creation
****************************************************************************************/
public with sharing class OS_StateModelParser extends OS_BaseParser
{
private OS_StateResponseModel responseModel {get; set;}
//--Parse the payload into the OS_StateResponseModel
public OS_StateResponseModel parse(String json)
{
//---Setup the return value
responseModel = new OS_StateResponseModel();
responseModel.States = new List<OS_StateModel>();
try
{
parseBody(json);
}
catch (Exception ex)
{
debug( 'Exception: ' + ex.getMessage() + ' ' + ex.getStackTraceString());
debugJSON(json);
}
return responseModel;
}
//---Parse the body of the JSON
private void parseBody(String json)
{
//---Set the default Page Size
if (PageSize == 0) PageSize = DEFAULT_PAGE_SIZE;
Boolean hasTokenTime = false;
Boolean hasTokenStates = false;
JSONParser parser = System.JSON.createParser(json);
//---Read Token by Token
while (parser.nextToken() != System.JSONToken.END_OBJECT)
{
//---Read the current Token, check its field name
if (parser.getCurrentToken() == System.JSONToken.FIELD_NAME)
{
String text = parser.getText();
debug( 'Json Token: ' + text);
//---If the next token has a value, then write it, this handles null values
if (parser.nextToken() != System.JSONToken.VALUE_NULL)
{
//---Read the time
if (text == 'time')
{
responseModel.ResponseTime = parser.getIntegerValue();
hasTokenTime = true;
}
//---Read the states
if (text == 'states')
{
responseModel.States = parseStateList(parser);
hasTokenStates = true;
}
}
}
//---If have both tags, then break out of loop
if (hasTokenStates && hasTokenTime) break;
}
}
//---Parse the States
private List<OS_StateModel> parseStateList(JSONParser parser)
{
CurrentRecord = 0;
//---Build the return list
List<OS_StateModel> retList = new List<OS_StateModel>();
//---Go to the next token if needed
if (parser.getCurrentToken() == null) parser.nextToken();
//---Iterate over the items
while (parser.nextToken() != System.JSONToken.END_ARRAY)
{
CurrentRecord++; //---Track the current record
String logLine = 'Rec: ' + CurrentRecord + ': ';
if (CurrentRecord > getMaxRecord())
{
//---If past the max record, then break out of loop
debug(logLine + 'End of page');
break;
}
else if (CurrentRecord >= getFirstRecordToProcess())
{
//---Build a new State Row
OS_StateModel sModel = new OS_StateModel();
parseStateItem(parser, sModel);
retList.add(sModel);
debug(logLine + 'State: ' + sModel);
}
else
{
//---Pass in null model to trigger token advancement without reading values
parseStateItem(parser, null);
debug(logLine + 'Skipped');
}
}
return retList;
}
//---Parse the States
private void parseStateItem(JSONParser parser, OS_StateModel sModel)
{
//---Go to the next token if needed
if (parser.getCurrentToken() == null) parser.nextToken();
Integer position = 0;
while (parser.nextToken() != System.JSONToken.END_ARRAY)
{
if (sModel != null && parser.getCurrentToken() != System.JSONToken.VALUE_NULL)
{
if (position == 0) sModel.icao24 = parser.getText();
if (position == 1) sModel.callsign = parser.getText();
if (position == 2) sModel.origin_country = parser.getText();
if (position == 3) sModel.time_position = parser.getIntegerValue();
if (position == 4) sModel.last_contact = parser.getIntegerValue();
if (position == 5) sModel.longitude = parser.getDoubleValue();
if (position == 6) sModel.latitude = parser.getDoubleValue();
if (position == 7) sModel.baro_altitude = parser.getDoubleValue();
if (position == 8) sModel.on_ground = parser.getBooleanValue();
if (position == 9) sModel.velocity = parser.getDoubleValue();
if (position == 10) sModel.true_track = parser.getDoubleValue();
if (position == 11) sModel.vertical_rate = parser.getDoubleValue();
if (position == 12)
{
//---Sensors, sub Array, should be null
}
if (position == 13) sModel.geo_altitude = parser.getDoubleValue();
if (position == 14) sModel.squawk = parser.getText();
if (position == 15) sModel.spi = parser.getBooleanValue();
if (position == 16) sModel.position_source = parser.getIntegerValue();
if (position == 17) sModel.category = parser.getIntegerValue();
}
position++;
//System.debug('Position ' + position);
}
}
}

Bagian 3: Pemetaan Dinamis dengan Custom Metadata

Jika Anda membutuhkan fleksibilitas yang lebih besar lagi, pertimbangkan pemetaan dinamis dengan custom metadata. Pendekatan ini memungkinkan Anda memetakan bidang JSON secara dinamis ke bidang SObject tanpa mengubah kode. Dengan pemetaan dinamis, Anda bisa menambah atau menghapus bidang secara langsung, sehingga sangat fleksibel di lingkungan produksi.

Contoh:

Buat custom metadata yang memetakan bidang JSON ke nama API, posisi, dan tipe data SObject.

Muat custom metadata itu dan pakai untuk menetapkan nilai ke bidang SObject secara dinamis.

/****************************************************************************************
* @Name OS_StateSObjectModelParser
* @Author Steve Simpson - steve@stevetecharc.com
* @Date 2023
*
* @Description Parser for reading State Models from JSON
****************************************************************************************
* Version Developer Date Description
*----------------------------------------------------------------------------------------
* 1.0 Steve Simpson 2023 Initial Creation
****************************************************************************************/
public with sharing class OS_StateSObjectModelParser extends OS_BaseParser
{
private OS_StateSObjectResponseModel responseModel {get; set;}
private List<OS_StateMap__mdt> mapList {get;set;}
//--Parse the payload into the OS_StateResponseModel
public OS_StateSObjectResponseModel parse(String json)
{
mapList = OS_StateMap__mdt.getAll().values();
//---Setup the return value
responseModel = new OS_StateSObjectResponseModel();
responseModel.States = new List<OS_State__c>();
try
{
parseBody(json);
}
catch (Exception ex)
{
debug( 'Exception: ' + ex.getMessage() + ' ' + ex.getStackTraceString());
debugJSON(json);
}
return responseModel;
}
//---Parse the body of the JSON
private void parseBody(String json)
{
//---Set the default Page Size
if (PageSize == 0) PageSize = DEFAULT_PAGE_SIZE;
Boolean hasTokenTime = false;
Boolean hasTokenStates = false;
JSONParser parser = System.JSON.createParser(json);
//---Read Token by Token
while (parser.nextToken() != System.JSONToken.END_OBJECT)
{
//---Read the current Token, check its field name
if (parser.getCurrentToken() == System.JSONToken.FIELD_NAME)
{
String text = parser.getText();
debug( 'Json Token: ' + text);
//---If the next token has a value, then write it, this handles null values
if (parser.nextToken() != System.JSONToken.VALUE_NULL)
{
//---Read the time
if (text == 'time')
{
responseModel.ResponseTime = parser.getIntegerValue();
hasTokenTime = true;
}
//---Read the states
if (text == 'states')
{
responseModel.States = parseStateList(parser);
hasTokenStates = true;
}
}
}
//---If have both tags, then break out of loop
if (hasTokenStates && hasTokenTime) break;
}
}
//---Parse the States
private List<OS_State__c> parseStateList(JSONParser parser)
{
CurrentRecord = 0;
//---Build the return list
List<OS_State__c> retList = new List<OS_State__c>();
//---Go to the next token if needed
if (parser.getCurrentToken() == null) parser.nextToken();
//---Iterate over the items
while (parser.nextToken() != System.JSONToken.END_ARRAY)
{
CurrentRecord++; //---Track the current record
String logLine = 'Rec: ' + CurrentRecord + ': ';
if (CurrentRecord > getMaxRecord())
{
//---If past the max record, then break out of loop
debug(logLine + 'End of page');
break;
}
else if (CurrentRecord >= getFirstRecordToProcess())
{
//---Build a new State Row
OS_State__c osState = new OS_State__c();
parseStateItem(parser, osState);
retList.add(osState);
debug(logLine + 'State: ' + osState);
}
else
{
//---Pass in null model to trigger token advancement without reading values
parseStateItem(parser, null);
debug(logLine + 'Skipped');
}
}
return retList;
}
//---Parse the States
private void parseStateItem(JSONParser parser, OS_State__c osState)
{
//---Go to the next token if needed
if (parser.getCurrentToken() == null) parser.nextToken();
Integer position = 0;
while (parser.nextToken() != System.JSONToken.END_ARRAY)
{
if (osState != null && parser.getCurrentToken() != System.JSONToken.VALUE_NULL)
{
OS_StateMap__mdt curMap = null;
for(OS_StateMap__mdt mapRow : mapList)
{
if (mapRow.Position__c == position)
{
curMap = mapRow;
break;
}
}
if (curMap != null)
{
String apiName = curMap.API_Name__c;
if (curMap.Data_Type__c == 'Text')
{
osState.put(apiName, parser.getText());
}
if (curMap.Data_Type__c == 'Integer')
{
osState.put(apiName, parser.getIntegerValue());
}
if (curMap.Data_Type__c == 'Double')
{
osState.put(apiName, parser.getDoubleValue());
}
if (curMap.Data_Type__c == 'Boolean')
{
osState.put(apiName, parser.getBooleanValue());
}
}
}
position++;
//System.debug('Position ' + position);
}
}
}

Kesimpulan:

Dalam callout Apex di Salesforce, Anda bisa memilih berbagai teknik penguraian JSON sesuai kebutuhan. Untuk JSON yang sederhana dan statis, pakai penguraian JSON tetap dengan deserialisasi satu baris. Untuk struktur yang lebih rumit, pertimbangkan penguraian JSON dinamis. Dan untuk fleksibilitas maksimal, pakai pemetaan dinamis dengan custom metadata. Setiap pendekatan punya kasus penggunaannya sendiri, jadi pilihlah yang paling sesuai dengan kebutuhan proyek Anda.

Selamat ngoding!

Nantikan Terus

Untuk wawasan dan tips lainnya, terus ikuti di www.SteveTechArc.com dan kanal YouTube @SteveTechArc (opens in a new tab). Berlangganan dan perdalam pemahaman Anda tentang Salesforce dan cara mengintegrasikannya dengan sistem lain.

Membantu mengubah dunia dengan berbagi informasi integrasi kepada sesama Arsitek dan mereka yang sedang menempuh Perjalanan Menjadi Arsitek!

Transkrip dibantu AI

STA 1.6