Come deserializzare un elenco con map in wrapperclass

Aug 18 2020

Ho una stringa che sto cercando di deserializzare in un oggetto / classe di involucro. Ma ottieni l'errore:

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

Corda

[
  {
    "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"
  }
]

Apice

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

Elenco passato al componente fulmine

 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;

Controller di fulmini

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

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

Controller di fulmini

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"

AGGIORNAMENTO ***** Per chiunque si imbattesse in questo problema ho capito che la risposta di seguito affronta il problema della mappa ma dopo aver reso la mappa la mappa è ancora alterata, quindi ho finito di combinare questa soluzione passando direttamente l'oggetto: Come passare un oggetto da un Componente Lightning helper per un metodo Apex Controller

  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); 

Risposte

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

Dovrebbe essere:

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

O:

@AuraEnabled global List<KeyValuePair> availableOptions;

Dov'è KeyValuePair:

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

Che è usato abbastanza comunemente nel codice da averlo come classe di primo livello nella mia organizzazione per sviluppatori; Lo uso abbastanza spesso per i valori degli elenchi di selezione e così via.

Questo tipo di JSON è generalmente sconsigliato, ma se devi usarlo in questo modo, questa è la forma preferita. Per poter utilizzare il tuo Apex originale, dovrebbe essere formattato come:

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

Questo è il modo in cui JSON deve essere formattato in senso generale, sebbene la forma precedente sia accettabile per casi come le opzioni dell'elenco di selezione, che hanno un valore e un'etichetta.


Sembra che tu debba trasformare le tue "opzioni disponibili" di nuovo in una mappa corretta da utilizzare correttamente:

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 nel tuo JSON non è una mappa, è un elenco di oggetti (con i campi "chiave" e "valore").

Quindi la tua classe wrapper deve riflettere questo. Invece di un Map<String, String>, devi renderlo un elenco di un'altra classe / tipo che può contenere i dati di destinazione ("chiave" e "valore")