JSON 支持

Apex 中的 JavaScript 对象表示法 (JSON) 支持可实现 Apex 的序列化 对象转换为JSON格式,并对序列化的JSON内容进行反序列化。Apex 提供了一组类,用于公开 JSON 序列化的方法和 反序列化。下表描述了可用的类。

描述
System.JSON包含用于将 Apex 对象序列化为 JSON 格式的方法,以及 反序列化使用此类中的方法序列化的 JSON 内容。serialize
System.JSONGenerator包含用于使用标准将对象序列化为 JSON 内容的方法 JSON 编码。
System.JSONParser表示 JSON 编码内容的分析器。

枚举包含标记 用于 JSON 解析。System.JSONToken

这些类中的方法会抛出一个 if 在执行过程中遇到问题。JSONExceptionJSON 支持注意事项

  • JSON 序列化和反序列化支持可用于 sObjects(标准 对象和自定义对象)、Apex 原语和集合类型、返回类型 数据库方法(例如 SaveResult 和 DeleteResult)和 Apex 实例 类。
  • 只有托管包的自定义对象(类型)才能从以下代码进行序列化: 托管包的外部。作为 中定义的 Apex 类实例的对象 无法序列化托管包。sObject
  • 仅当 Map 对象使用 以下数据类型作为键。
    • 布尔
    • 日期
    • 日期时间
    • 十进制
    • 枚举
    • 同上
    • 整数
    • 字符串
    • 时间
  • 当对象被声明为父类型,但被设置为 子类型,某些数据可能会丢失。该对象被序列化和反序列化为 父类型和特定于子类型的任何字段都将丢失。
  • 具有自身引用的对象不会被序列化,并导致抛出。JSONException
  • 两次引用同一对象的引用图将被反序列化并导致 要生成的引用对象的多个副本。
  • 数据类型不是 序列 化。如果尝试创建可序列化类的实例,例如 Visualforce 控制器,其成员变量类型为 ,您会收到异常。若要在可序列化类中使用,请使用 local 变量。System.JSONParserSystem.JSONParserJSONParser

版本化行为更改

在 API 版本 53.0 及更高版本中,DateTime 格式和处理已更新。The API 正确处理 JSON 请求中使用 3 位以上数字的 JSON请求中的DateTime值 小数点。使用不支持的 DateTime 格式(如 )的请求会导致错误。Salesforce 建议您 严格遵守 https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_valid_date_formats.htm 中指定的 DateTime 格式。123456000

  • 往返序列化和反序列化
    使用类方法对 JSON 内容执行往返序列化和反序列化。通过这些方法,可以将对象序列化为 JSON 格式的字符串,并将 JSON 字符串反序列化回对象。JSON
  • JSON 生成器
    使用类方法,可以生成标准的 JSON 编码内容。JSONGenerator
  • JSON 解析
    使用类方法解析 JSON 编码的内容。通过这些方法,可以分析从对外部服务(如 Web 服务标注)的调用返回的 JSON 格式的响应。JSONParser

往返序列化和反序列化

使用类方法执行往返 JSON 内容的序列化和反序列化。这些方法使您能够 将对象序列化为 JSON 格式的字符串,并将 JSON 字符串反序列化回 对象。

JSON

示例:序列化和反序列化发票列表

此示例创建一个对象列表并序列化该列表。接下来,序列化 JSON 字符串用于再次反序列化列表,示例验证 新列表包含与原始列表中相同的发票 列表。

InvoiceStatement

public class JSONRoundTripSample {
  
    public class InvoiceStatement {
        Long invoiceNumber;
        Datetime statementDate;
        Decimal totalPrice;
        
        public InvoiceStatement(Long i, Datetime dt, Decimal price)
        {
            invoiceNumber = i;
            statementDate = dt;
            totalPrice = price;
        }
    }
    
    public static void SerializeRoundtrip() {
        Datetime dt = Datetime.now(); 
        // Create a few invoices.
        InvoiceStatement inv1 = new InvoiceStatement(1,Datetime.valueOf(dt),1000);
        InvoiceStatement inv2 = new InvoiceStatement(2,Datetime.valueOf(dt),500);
        // Add the invoices to a list.
        List<InvoiceStatement> invoices = new List<InvoiceStatement>();
        invoices.add(inv1);
        invoices.add(inv2);
              
        // Serialize the list of InvoiceStatement objects.
        String JSONString = JSON.serialize(invoices);
        System.debug('Serialized list of invoices into JSON format: ' + JSONString);
        
        // Deserialize the list of invoices from the JSON string.
        List<InvoiceStatement> deserializedInvoices = 
          (List<InvoiceStatement>)JSON.deserialize(JSONString, List<InvoiceStatement>.class);
        System.assertEquals(invoices.size(), deserializedInvoices.size());
        Integer i=0;
        for (InvoiceStatement deserializedInvoice :deserializedInvoices) {
            system.debug('Deserialized:' + deserializedInvoice.invoiceNumber + ',' 
            + deserializedInvoice.statementDate.formatGmt('MM/dd/yyyy  HH:mm:ss.SSS')
            + ', ' + deserializedInvoice.totalPrice); 
            system.debug('Original:' + invoices[i].invoiceNumber + ',' 
            + invoices[i].statementDate.formatGmt('MM/dd/yyyy  HH:mm:ss.SSS') 
            + ', ' + invoices[i].totalPrice); 
            i++;
        }
    }
}

JSON 序列化注意事项

该方法的行为不同 取决于保存的 Apex 代码的 Salesforce API 版本。serialize使用设置其他字段的查询 sObject 的序列化对于使用 Salesforce API 版本 27.0 及更早版本保存的 Apex,如果查询 sObjects 设置了其他字段,这些字段不包含在 方法返回的序列化 JSON 字符串。从使用 Salesforce 保存的 Apex 开始 API 版本 28.0,其他字段包含在序列化的 JSON 中 字符串。serialize本示例在查询联系人后向该联系人添加一个字段,然后 序列化联系人。断言语句验证 JSON string 包含附加字段。断言传递 Apex 保存 使用 Salesforce API 版本 28.0 和 后。

Contact con = [SELECT Id, LastName, AccountId FROM Contact LIMIT 1]; 
// Set additional field
con.FirstName = 'Joe'; 
String jsonstring = Json.serialize(con); 
System.debug(jsonstring); 
System.assert(jsonstring.contains('Joe') == true);

聚合查询结果字段的序列化对于使用 Salesforce API 版本 27.0 保存的 Apex,聚合结果 在以下情况下,查询不包括 SELECT 语句中的字段 使用该方法序列化。对于早期 API 版本或 API 版本 28.0 及更高版本, 序列化聚合查询结果包括 SELECT 中的所有字段 陈述。serialize此聚合查询返回两个字段:ID 字段计数和 帐户名称。

String jsonString = JSON.serialize(
    Database.query('SELECT Count(Id),Account.Name FROM Contact WHERE Account.Name != null GROUP BY Account.Name LIMIT 1'));
    System.debug(jsonString);

// Expected output in API v 26 and earlier or v28 and later
// [{"attributes":{"type":"AggregateResult"},"expr0":2,"Name":"acct1"}]

空字段的序列化从 API 版本 28.0 开始,null 字段不会序列化,也不会 包含在 JSON 字符串中,这与早期版本不同。此更改不会 影响使用 JSON 方法(例如 Json.deserialize())反序列化 JSON 字符串。此更改是 检查 JSON 字符串时会注意到这一点。为 例:

String jsonString = JSON.serialize(
                 [SELECT Id, Name, Website FROM Account WHERE Website = null LIMIT 1]);
System.debug(jsonString);

// In v27.0 and earlier, the string includes the null field and looks like the following.
// {"attributes":{...},"Id":"001D000000Jsm0WIAR","Name":"Acme","Website":null}

// In v28.0 and later, the string doesn’t include the null field and looks like 
//  the following.
// {"attributes":{...},"Name":"Acme","Id":"001D000000Jsm0WIAR"}}

ID 序列化在 API 版本 34.0 及更早版本中,对于已通过 往返 JSON 序列化和反序列化。==

JSON 反序列化注意事项

聚合结果中的 JSON 无法反序列化回 Apex AggregateResult 对象,因为它们没有命名字段。

JSON生成器

使用类方法,可以生成标准的 JSON 编码内容。

JSONGenerator

您可以使用标准逐个元素构造 JSON 内容 JSON 编码。为此,请使用类中的方法。JSONGenerator

JSONGenerator 示例

此示例生成 使用类的方法以漂亮的打印格式创建 JSON 字符串。首先举个例子 添加一个数字字段和一个字符串字段,然后添加一个要包含的字段 整数列表的 object 字段,该字段已正确反序列化。 接下来,它添加对象 进入现场,其中 也会被反序列化。JSONGeneratorAObject A

public class JSONGeneratorSample{

    public class A { 
        String str;
        
        public A(String s) { str = s; }
    }

    static void generateJSONContent() {
        // Create a JSONGenerator object.
        // Pass true to the constructor for pretty print formatting.
        JSONGenerator gen = JSON.createGenerator(true);
        
        // Create a list of integers to write to the JSON string.
        List<integer> intlist = new List<integer>();
        intlist.add(1);
        intlist.add(2);
        intlist.add(3);
        
        // Create an object to write to the JSON string.
        A x = new A('X');
        
        // Write data to the JSON string.
        gen.writeStartObject();
        gen.writeNumberField('abc', 1.21);
        gen.writeStringField('def', 'xyz');
        gen.writeFieldName('ghi');
        gen.writeStartObject();
        
        gen.writeObjectField('aaa', intlist);
        
        gen.writeEndObject();
        
        gen.writeFieldName('Object A');
        
        gen.writeObject(x);
        
        gen.writeEndObject();
        
        // Get the JSON string.
        String pretty = gen.getAsString();
        
        System.assertEquals('{\n' +
        '  "abc" : 1.21,\n' +
        '  "def" : "xyz",\n' +
        '  "ghi" : {\n' +
        '    "aaa" : [ 1, 2, 3 ]\n' +
        '  },\n' +
        '  "Object A" : {\n' +
        '    "str" : "X"\n' +
        '  }\n' +
        '}', pretty);
    }
}

JSON解析

使用类方法进行解析 JSON 编码的内容。这些方法使你能够分析 JSON 格式的响应,该响应是 从对外部服务(如 Web 服务标注)的调用返回。

JSONParser

以下示例演示如何分析 JSON 字符串。

示例:解析来自 Web 服务标注的 JSON 响应

此示例使用方法分析 JSON 格式的响应。它对返回 JSON 格式的响应。接下来,解析响应以从 api 构建映射 版本号添加到版本标签中。JSONParser

public class JSONParserUtil {
    public static void parseJSONResponse() {        
        
        // Create HTTP request to send.
        HttpRequest request = new HttpRequest();
        // Set the endpoint URL.
        String endpoint = URL.getOrgDomainUrl().toExternalForm() + '/services/data';
        request.setEndPoint(endpoint);
        // Set the HTTP verb to GET.
        request.setMethod('GET');
        // Set the request header for JSON content type
        request.setHeader('Accept', 'application/json');
        
        // Send the HTTP request and get the response.
        // The response is in JSON format.
        Http httpProtocol = new Http();
        HttpResponse response = httpProtocol.send(request);
        System.debug(response.getBody());
        /* The JSON response returned is the following:
            {"label":"Summer '14","url":"/services/data/v31.0","version":"31.0"},
            {"label":"Winter '15","url":"/services/data/v32.0","version":"32.0"},
            {"label":"Spring '15","url":"/services/data/v33.0","version":"33.0"},
        */
        // Parse JSON response to build a map from API version numbers to labels
        JSONParser parser = JSON.createParser(response.getBody());
        Map<double, string> apiVersionToReleaseNameMap = new Map<double, string>();
        
        string label = null;
        double version = null;
        
        while (parser.nextToken() != null) {
            
            if (parser.getCurrentToken() == JSONToken.FIELD_NAME) {
                switch on parser.getText() {
                    when 'label' {
                    // Advance to the label value.
                    parser.nextToken();
                        label = parser.getText();
                    }
                    when 'version' {
                        // Advance to the version value.
                        parser.nextToken();
                        version = Double.valueOf(parser.getText());
                    }
                }
            }
            
            if(version != null && String.isNotEmpty(label)) {
                apiVersionToReleaseNameMap.put(version, label);
                version = null;
                label = null; 
            }
        }
        system.debug('Release with Rainbow logo = ' +
            apiVersionToReleaseNameMap.get(39.0D));
    }
}

示例:解析 JSON 字符串并将其反序列化为对象

此示例使用硬编码的 JSON 字符串,该字符串与 返回的 JSON 字符串相同 上一示例中的标注。在此示例中,将分析整个字符串 使用该方法添加到对象中。 此代码还使用 跳过子数组和子对象,并解析 列表。分析的对象是定义为内部类的类的实例。因为每张发票 包含行项,表示相应行项类型的类,该类,也被定义为 内部类。将此示例代码添加到要使用的类中 它。

InvoicereadValueAsskipChildrenInvoiceLineItem

public static void parseJSONString() {
    String jsonStr = 
        '{"invoiceList":[' +
        '{"totalPrice":5.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' +
            '{"UnitPrice":1.0,"Quantity":5.0,"ProductName":"Pencil"},' +
            '{"UnitPrice":0.5,"Quantity":1.0,"ProductName":"Eraser"}],' +
                '"invoiceNumber":1},' +
        '{"totalPrice":11.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' +
            '{"UnitPrice":6.0,"Quantity":1.0,"ProductName":"Notebook"},' +
            '{"UnitPrice":2.5,"Quantity":1.0,"ProductName":"Ruler"},' +
            '{"UnitPrice":1.5,"Quantity":2.0,"ProductName":"Pen"}],"invoiceNumber":2}' +
        ']}';

    // Parse entire JSON response.
    JSONParser parser = JSON.createParser(jsonStr);
    while (parser.nextToken() != null) {
        // Start at the array of invoices.
        if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
            while (parser.nextToken() != null) {
                // Advance to the start object marker to
                //  find next invoice statement object.
                if (parser.getCurrentToken() == JSONToken.START_OBJECT) {
                    // Read entire invoice object, including its array of line items.
                    Invoice inv = (Invoice)parser.readValueAs(Invoice.class);
                    system.debug('Invoice number: ' + inv.invoiceNumber);
                    system.debug('Size of list items: ' + inv.lineItems.size());
                    // For debugging purposes, serialize again to verify what was parsed.
                    String s = JSON.serialize(inv);
                    system.debug('Serialized invoice: ' + s);

                    // Skip the child start array and start object markers.
                    parser.skipChildren();
                }
            }
        }
    }
} 

// Inner classes used for serialization by readValuesAs(). 

public class Invoice {
    public Double totalPrice;
    public DateTime statementDate;
    public Long invoiceNumber;
    List<LineItem> lineItems;
    
    public Invoice(Double price, DateTime dt, Long invNumber, List<LineItem> liList) {
        totalPrice = price;
        statementDate = dt;
        invoiceNumber = invNumber;
        lineItems = liList.clone();
    }
}  

public class LineItem {
    public Double unitPrice;
    public Double quantity;
    public String productName;
}