Được dịch bằng AI. Cộng đồng chưa xem xét. Đọc bằng tiếng Anh

Blog · 13 tháng 9, 2023 · Steve Simpson · Tích hợp

JSON Jamboree: Cuộc đối đầu giữa giải tuần tự hóa cố định và động cho Apex callout

Giới thiệu:

Xin chào! Tôi là Steve Simpson, một Salesforce Certified Technical Architect và giảng viên. Trong bài blog này, chúng ta sẽ khám phá các kỹ thuật khác nhau để phân tích JSON khi thực hiện Apex callout trong Salesforce. Chúng ta sẽ thảo luận về phân tích JSON cố định, phân tích JSON động, và một cách tiếp cận lai mang lại sự linh hoạt và dễ sử dụng.

Phần 1: Phân tích JSON cố định với giải tuần tự hóa một dòng

Khi xử lý các cấu trúc JSON đơn giản và ít thay đổi, bạn có thể tạo một lớp Apex khớp chính xác với cấu trúc JSON. Cách này cho phép bạn dùng một dòng code duy nhất để giải tuần tự hóa chuỗi JSON thành một mảng. Phương pháp này mạnh mẽ và hiệu quả với các cấu trúc JSON tĩnh.

Ảnh chụp màn hình: tài liệu của API chuyến bay đến OpenSky, với các tham số yêu cầu Ảnh chụp màn hình Postman: một lệnh gọi GET đến API chuyến bay đến OpenSky trả về dữ liệu chuyến bay dạng 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);
}
}

Với lớp ở trên, bạn có thể dễ dàng phân tích JSON bằng một dòng code duy nhất:

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

Phần 2: Phân tích JSON động

Với các cấu trúc JSON phức tạp hơn, như mảng hay đối tượng lồng nhau, bạn có thể cần dùng phân tích JSON động. Cách này đòi hỏi nhiều dòng code hơn và cẩn thận điều hướng qua các token JSON. Dù có vẻ phức tạp hơn, phân tích JSON động là cần thiết khi xử lý các cấu trúc JSON nâng cao.

Ví dụ:

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

Phần 3: Ánh xạ động với custom metadata

Nếu bạn cần linh hoạt hơn nữa, hãy cân nhắc dùng ánh xạ động với custom metadata. Cách này cho phép bạn ánh xạ các trường JSON sang trường SObject một cách động mà không cần thay đổi code. Với ánh xạ động, bạn có thể thêm hay bớt trường ngay lập tức, mang lại sự linh hoạt lớn trong môi trường thật.

Ví dụ:

Tạo custom metadata ánh xạ các trường JSON sang tên API, vị trí và kiểu dữ liệu của SObject.

Nạp custom metadata và dùng nó để gán giá trị cho các trường SObject một cách động.

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

Kết luận:

Trong Apex callout của Salesforce, bạn có thể chọn từ nhiều kỹ thuật phân tích JSON tùy theo nhu cầu. Với JSON tĩnh, đơn giản, hãy dùng phân tích JSON cố định với giải tuần tự hóa một dòng. Với các cấu trúc phức tạp hơn, hãy cân nhắc phân tích JSON động. Và để linh hoạt tối đa, hãy dùng ánh xạ động với custom metadata. Mỗi cách tiếp cận có những trường hợp sử dụng riêng, nên hãy chọn cách phù hợp nhất với yêu cầu dự án của bạn.

Chúc bạn code vui vẻ!

Hãy đón xem

Để có thêm góc nhìn và mẹo hay, hãy theo dõi tại www.SteveTechArc.com và kênh YouTube @SteveTechArc (opens in a new tab). Hãy đăng ký và nâng cao hiểu biết của bạn về Salesforce và cách tích hợp nó với các hệ thống khác.

Góp phần thay đổi thế giới bằng cách chia sẻ kiến thức tích hợp với các kiến trúc sư đồng nghiệp và những ai đang trên hành trình trở thành kiến trúc sư!

Bản ghi lời thoại được AI hỗ trợ

STA 1.6