Cómo deserializar la lista con map to wrapperclass

Aug 18 2020

Tengo una cadena que estoy tratando de deserializar en un contenedor / clase de objeto. Pero obtén el error:

Expected Map<String,String> but found [line:1, column:3]

Wrapperclass

global class Invocable_Obj_HandleInvoice  {
    @AuraEnabled global Id invoiceId;
    @AuraEnabled global string invoiceNumber;
    @AuraEnabled global decimal balanceAmount;
    @AuraEnabled global decimal totalAmount;
    @AuraEnabled global string customerName;
    @AuraEnabled global string zuoraStatus;
    @AuraEnabled global string handlingStatus;
    @AuraEnabled global string selectedOption;
    @AuraEnabled global Map<String, String> availableOptions;
}

Cuerda

[
  {
    "availableOptions": [
      {
        "key": "e",
        "value": "f"
      },
      {
        "key": "g",
        "value": "z"
      }
    ],
    "balanceAmount": -100,
    "customerName": "Test",
    "handlingStatus": "New",
    "invoiceId": "a0S2o000023CSHoEAO",
    "invoiceNumber": "INV-001",
    "selectedOption": "A",
    "totalAmount": -100,
    "zuoraStatus": "Posted"
  },
  {
    "availableOptions": [
      {
        "key": "a",
        "value": "x"
      },
      {
        "key": "b",
        "value": "y"
      },
      {
        "key": "c",
        "value": "z"
      }
    ],
    "balanceAmount": -100,
    "customerName": "Test",
    "handlingStatus": "New",
    "invoiceId": "a0S2o000023CSHoEAO",
    "invoiceNumber": "INV-001",
    "selectedOption": "y",
    "totalAmount": -100,
    "zuoraStatus": "Posted"
  }
]

Apéndice

 system.debug((List<Invocable_Obj_HandleInvoice>) System.JSON.deserialize(invoices, List<Invocable_Obj_HandleInvoice>.class));  

Lista pasada al componente relámpago

 List<Invocable_Obj_HandleInvoice> hiList = new List<Invocable_Obj_HandleInvoice>();
    Invocable_Obj_HandleInvoice  hi1 =  new Invocable_Obj_HandleInvoice(); 
    hi1.invoiceId = zi[0].id;
    hi1.invoiceNumber = zi[0].Name; 
    hi1.balanceAmount = zi[0].Zuora__Balance2__c;
    hi1.totalAmount = zi[0].Zuora__TotalAmount__c;
    hi1.customerName = 'Test';
    hi1.zuoraStatus = zi[0].Zuora__Status__c; 
    hi1.handlingStatus = zi[0].Handling_Status__c;
    hi1.selectedOption = 'A'; 
    hi1.availableOptions = new Map<String,string> {'e'=>'f', 'g'=> 'z'}; 
    hiList.add(hi1); 
return hiList;

Controlador de rayos

var act = cmp.get("c.updateInvoices");
var updatedInvoiceList = JSON.stringify(cmp.get('v.handleInvoices'));
act.setParams({ "invoices" : updatedInvoiceList});
$A.enqueueAction(act); 

Componente <aura: atributo type = "List" name = "handleInvoices" />

Controlador de rayos

doInit : function(cmp, event, helper) {
        var action = cmp.get("c.testHandlInvoice");
        
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                cmp.set('v.handleInvoices', response.getReturnValue());
                console.log(cmp.get('v.handleInvoices'));
            }
            
        });
        $A.enqueueAction(action);
    },

console.log

availableOptions: Array(3)
0: {key: "a", value: "x"}
1: {key: "b", value: "y"}
2: {key: "c", value: "z"}
length: 3
__proto__: Array(0)
balanceAmount: -100
customerName: "Test"
handlingStatus: "New"
invoiceId: "a0S2o000023CSHoEAO"
invoiceNumber: "INV-001"
selectedOption: "A"
totalAmount: -100
zuoraStatus: "Posted"

ACTUALIZACIÓN ***** Para cualquiera que se encuentre con este problema, descubrí que la respuesta a continuación aborda el problema del mapa, pero después de la secuenciación, el mapa aún está alterado, así que terminé combinando esta solución pasando el objeto directamente: Cómo pasar un objeto desde un Asistente de componente Lightning para un método de controlador de Apex

  var act = cmp.get("c.updateInvoices");
        var tempInvoiceList = cmp.get('v.handleInvoices');
        tempInvoiceList = tempInvoiceList.slice(); // copy
        tempInvoiceList = tempInvoiceList.map(function(invoice) { return Object.assign({}, invoice); }); // copy deeper
        act.setParams({ "invoices" : tempInvoiceList});
        tempInvoiceList = tempInvoiceList
        .forEach(function(invoice) { 
            invoice.availableOptions = invoice.availableOptions
            .reduce(function(prev, option) { 
                prev[option.key] = option.value; 
                return prev;
            }, 
                    {})
        }
                );
        $A.enqueueAction(act); 

Respuestas

3 sfdcfox Aug 18 2020 at 19:40
@AuraEnabled global Map<String, String> availableOptions;

Debiera ser:

@AuraEnabled global List<Map<String,String>> availableOptions;

O:

@AuraEnabled global List<KeyValuePair> availableOptions;

Donde KeyValuePairesta:

public class KeyValuePair {
  @AuraEnabled public String key;
  @AuraEnabled public String value;
}

Lo cual se usa con suficiente frecuencia en el código que realmente lo tengo como una clase de nivel superior en mi organización de desarrollo; Lo uso con bastante frecuencia para valores de listas de selección, etc.

Este tipo de JSON generalmente no se recomienda, pero si tiene que usarlo de esa manera, esta es la forma preferida. Para utilizar su Apex original, debe formatearse como:

"availableOptions": { "e": "f", "g", "z" },

Así es como JSON debe formatearse en el sentido general, aunque la forma anterior es aceptable para casos como opciones de lista de selección, que tienen un valor y una etiqueta.


Parece que necesita transformar sus "opciones disponibles" de nuevo en un mapa adecuado para utilizarlo correctamente:

var act = cmp.get("c.updateInvoices");
var tempInvoiceList = cmp.get('v.handleInvoices');
tempInvoiceList = tempInvoiceList.slice(); // copy
tempInvoiceList = tempInvoiceList.map(function(invoice) { return Object.assign({}, invoice); }); // copy deeper
tempInvoiceList = tempInvoiceList
  .forEach(function(invoice) { 
    invoice.availableOptions = invoice.availableOptions
      .reduce(function(prev, option) { 
         prev[option.key] = option.value; 
         return prev;
      }, 
      {})
    }
  );
var updatedInvoiceList = JSON.stringify(tempInvoiceLIst);
act.setParams({ "invoices" : updatedInvoiceList});
$A.enqueueAction(act); 
DerekF Aug 18 2020 at 19:41

availableOptions en su JSON no es un mapa, es una lista de objetos (con campos "clave" y "valor").

Entonces, su clase contenedora debe reflejar eso. En lugar de a Map<String, String>, debe convertirlo en una Lista de otra clase / tipo que pueda contener los datos de destino ("clave" y "valor")