แปลโดย AI ชุมชนยังไม่ได้ตรวจทาน อ่านเป็นภาษาอังกฤษ

บล็อก · 13 กันยายน 2566 · Steve Simpson · การผสานรวม

JSON Jamboree: ศึกชิงชัย Deserialization แบบคงที่กับแบบไดนามิกสำหรับ Apex Callout

บทนำ:

สวัสดีครับ! ผม Steve Simpson, Salesforce Certified Technical Architect และผู้สอน ในบล็อกนี้ เราจะสำรวจเทคนิคต่างๆ ในการแยกวิเคราะห์ JSON เมื่อทำ Apex callout ใน Salesforce เราจะพูดถึงการแยกวิเคราะห์ JSON แบบคงที่ แบบไดนามิก และแนวทางผสมที่ให้ทั้งความยืดหยุ่นและความง่าย

ส่วนที่ 1: การแยกวิเคราะห์ JSON แบบคงที่ด้วย Deserialization บรรทัดเดียว

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

ภาพหน้าจอ: เอกสารของ OpenSky flight arrivals API พร้อมพารามิเตอร์ของคำขอ ภาพหน้าจอ Postman: การเรียก GET ไปยัง OpenSky arrivals API ที่คืนข้อมูลเที่ยวบินเป็น 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);
}
}

ด้วยคลาสข้างต้น คุณแยกวิเคราะห์ JSON ได้ง่ายๆ ด้วยโค้ดบรรทัดเดียว:

//---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;
}

ส่วนที่ 2: การแยกวิเคราะห์ JSON แบบไดนามิก

สำหรับโครงสร้าง JSON ที่ซับซ้อนขึ้น เช่น อาร์เรย์หรือออบเจกต์ซ้อน คุณอาจต้องใช้การแยกวิเคราะห์ JSON แบบไดนามิก วิธีนี้ต้องใช้โค้ดหลายบรรทัดขึ้นและการไล่ไปตาม token ของ JSON อย่างระมัดระวัง แม้อาจดูซับซ้อนกว่า การแยกวิเคราะห์ JSON แบบไดนามิกก็จำเป็นเมื่อจัดการกับโครงสร้าง JSON ขั้นสูง

ตัวอย่าง:

/****************************************************************************************
* @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);
}
}
}

ส่วนที่ 3: การจับคู่แบบไดนามิกด้วย Custom Metadata

ถ้าคุณต้องการความยืดหยุ่นมากขึ้นอีก ให้พิจารณาการจับคู่แบบไดนามิกด้วย custom metadata วิธีนี้ให้คุณจับคู่ฟิลด์ JSON กับฟิลด์ SObject แบบไดนามิกโดยไม่ต้องเปลี่ยนโค้ด ด้วยการจับคู่แบบไดนามิก คุณเพิ่มหรือลบฟิลด์ได้ทันที ให้ความยืดหยุ่นสูงในสภาพแวดล้อม production

ตัวอย่าง:

สร้าง custom metadata ที่จับคู่ฟิลด์ JSON กับชื่อ API ของ SObject ตำแหน่ง และประเภทข้อมูล

โหลด custom metadata แล้วใช้มันกำหนดค่าให้ฟิลด์ SObject แบบไดนามิก

/****************************************************************************************
* @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);
}
}
}

บทสรุป:

ใน Apex callout ของ Salesforce คุณเลือกเทคนิคการแยกวิเคราะห์ JSON ได้หลายแบบตามความต้องการ สำหรับ JSON ที่ตรงไปตรงมาและคงที่ ใช้การแยกวิเคราะห์แบบคงที่ด้วย deserialization บรรทัดเดียว สำหรับโครงสร้างที่ซับซ้อนขึ้น พิจารณาการแยกวิเคราะห์ JSON แบบไดนามิก และเพื่อความยืดหยุ่นสูงสุด ใช้การจับคู่แบบไดนามิกด้วย custom metadata แต่ละวิธีมีกรณีการใช้งานของตัวเอง จึงเลือกแบบที่เหมาะกับความต้องการของโปรเจกต์คุณที่สุด

ขอให้สนุกกับการเขียนโค้ด!

ติดตามต่อ

สำหรับมุมมองและเคล็ดลับเพิ่มเติม ติดตามได้ที่ www.SteveTechArc.com และช่อง YouTube @SteveTechArc (opens in a new tab) กดติดตามและเพิ่มความเข้าใจเรื่อง Salesforce และวิธีผสานรวมมันกับระบบอื่น

ช่วยเปลี่ยนโลกด้วยการแบ่งปันความรู้ด้านการผสานรวมกับเพื่อนสถาปนิกและผู้ที่อยู่บนเส้นทางสู่การเป็นสถาปนิก!

บทถอดความได้รับความช่วยเหลือจาก AI

STA 1.6