fix: Risolve errori di deserializzazione JSON con valori decimali in Salesforce API
- Aggiunge configurazione JsonSerializerOptions per garantire compatibilità con Salesforce - Implementa normalizzazione automatica di valori numerici con virgola decimale (es. "0,00" → 0.00) - Sostituisce PostAsJsonAsync con StringContent personalizzato per controllo completo della serializzazione - Aggiunge metodi NormalizeNumericValues e IsNumericWithComma per conversione formato decimale - Aggiorna TransformValue per utilizzare InvariantCulture nelle conversioni numeriche - Applica normalizzazione in CreateEntityAsync e metodi Composite (BatchCreate/BatchUpdate) - Aggiunge logging dettagliato per tracciare le normalizzazioni numeriche Risolve: "Impossibile deserializzare l'istanza di double da VALUE_STRING valore 0,00" - Il problema era causato dall'invio di valori decimali nel formato italiano (virgola) invece del formato americano (punto) richiesto da Salesforce API - La soluzione garantisce che tutti i valori numerici vengano sempre serializzati nel formato corretto per Salesforce indipendentemente dalla cultura locale
This commit is contained in:
Vendored
+16
@@ -27,6 +27,22 @@
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/Data_Coupler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Publish Data_Coupler",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"publish",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--output",
|
||||
"${workspaceFolder}/publish",
|
||||
"--project",
|
||||
"Data_Coupler/Data_Coupler.csproj"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,6 +23,17 @@ namespace DataConnection.REST.Implementations
|
||||
private string? _instanceUrl;
|
||||
private DateTime _tokenExpiry;
|
||||
|
||||
/// <summary>
|
||||
/// Configurazione JSON per garantire la compatibilità con Salesforce API
|
||||
/// Utilizza sempre la cultura invariante per i numeri per evitare problemi con virgole/punti decimali
|
||||
/// </summary>
|
||||
private static readonly JsonSerializerOptions SalesforceJsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
};
|
||||
|
||||
public SalesforceServiceClient(HttpClient httpClient, RestServiceOptions options)
|
||||
: base(httpClient, options)
|
||||
{
|
||||
@@ -392,9 +403,19 @@ namespace DataConnection.REST.Implementations
|
||||
Console.WriteLine($"--- Salesforce Entity Creation Attempt ---");
|
||||
Console.WriteLine($"SObject: {entityName}");
|
||||
Console.WriteLine($"Target URL: {createUri}");
|
||||
Console.WriteLine($"Data: {System.Text.Json.JsonSerializer.Serialize(entityData)}");
|
||||
Console.WriteLine($"Data: {JsonSerializer.Serialize(entityData, SalesforceJsonOptions)}");
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync(createUri, entityData, cancellationToken);
|
||||
// Normalizza i valori numerici per evitare problemi con virgole decimali
|
||||
var normalizedData = NormalizeNumericValues(entityData);
|
||||
|
||||
// Usa StringContent con configurazione JSON specifica per Salesforce
|
||||
var jsonContent = new StringContent(
|
||||
JsonSerializer.Serialize(normalizedData, SalesforceJsonOptions),
|
||||
System.Text.Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
var response = await _httpClient.PostAsync(createUri, jsonContent, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@@ -414,7 +435,7 @@ namespace DataConnection.REST.Implementations
|
||||
return entityData; // Return original data if no response content
|
||||
|
||||
// Salesforce returns creation result with Id and success status
|
||||
var creationResult = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(responseContent);
|
||||
var creationResult = JsonSerializer.Deserialize<Dictionary<string, object>>(responseContent, SalesforceJsonOptions);
|
||||
|
||||
// Merge the original data with the creation result (which includes the new Id)
|
||||
if (creationResult != null)
|
||||
@@ -653,6 +674,76 @@ namespace DataConnection.REST.Implementations
|
||||
return await AuthenticateAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizza i valori numerici in un dictionary per garantire compatibilità con Salesforce API
|
||||
/// Converte valori decimali con virgola in formato con punto decimale
|
||||
/// </summary>
|
||||
private static Dictionary<string, object> NormalizeNumericValues(Dictionary<string, object> data)
|
||||
{
|
||||
var normalizedData = new Dictionary<string, object>();
|
||||
bool hasNormalized = false;
|
||||
|
||||
foreach (var kvp in data)
|
||||
{
|
||||
var value = kvp.Value;
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
// Se è una stringa che rappresenta un numero decimale con virgola, convertila
|
||||
if (value is string stringValue && IsNumericWithComma(stringValue))
|
||||
{
|
||||
if (decimal.TryParse(stringValue, System.Globalization.NumberStyles.Number,
|
||||
System.Globalization.CultureInfo.CurrentCulture, out decimal decimalValue))
|
||||
{
|
||||
// Converte in double usando cultura invariante per garantire punto decimale
|
||||
var normalizedValue = double.Parse(decimalValue.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
normalizedData[kvp.Key] = normalizedValue;
|
||||
Console.WriteLine($"NUMERIC NORMALIZATION: {kvp.Key}: '{stringValue}' → {normalizedValue}");
|
||||
hasNormalized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedData[kvp.Key] = value;
|
||||
}
|
||||
}
|
||||
// Se è già un decimal, convertilo in double con cultura invariante
|
||||
else if (value is decimal dec)
|
||||
{
|
||||
var normalizedValue = double.Parse(dec.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
normalizedData[kvp.Key] = normalizedValue;
|
||||
Console.WriteLine($"DECIMAL NORMALIZATION: {kvp.Key}: {dec} → {normalizedValue}");
|
||||
hasNormalized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedData[kvp.Key] = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedData[kvp.Key] = value!;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNormalized)
|
||||
{
|
||||
Console.WriteLine($"NORMALIZATION SUMMARY: Processed {data.Count} fields, normalized {normalizedData.Count(kvp => kvp.Value is double)} numeric values");
|
||||
}
|
||||
|
||||
return normalizedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se una stringa rappresenta un numero con virgola decimale
|
||||
/// </summary>
|
||||
private static bool IsNumericWithComma(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return false;
|
||||
|
||||
// Pattern per numeri con virgola: opzionale segno, cifre, virgola, cifre
|
||||
return System.Text.RegularExpressions.Regex.IsMatch(value.Trim(), @"^[+-]?\d+,\d+$");
|
||||
}
|
||||
|
||||
// --- Nested classes for deserializing Salesforce responses ---
|
||||
private class SalesforceTokenResponse
|
||||
{
|
||||
@@ -928,17 +1019,27 @@ namespace DataConnection.REST.Implementations
|
||||
|
||||
for (int i = 0; i < batch.Count; i++)
|
||||
{
|
||||
// Normalizza i valori numerici per evitare problemi con virgole decimali
|
||||
var normalizedData = NormalizeNumericValues(batch[i]);
|
||||
|
||||
var subrequest = new SalesforceCompositeSubRequest
|
||||
{
|
||||
Method = "POST",
|
||||
Url = $"/services/data/v60.0/sobjects/{entityName}/",
|
||||
ReferenceId = $"create_{startIndex + i}",
|
||||
Body = batch[i]
|
||||
Body = normalizedData
|
||||
};
|
||||
compositeRequest.CompositeRequest.Add(subrequest);
|
||||
}
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync(compositeUri, compositeRequest, cancellationToken);
|
||||
// Usa StringContent con configurazione JSON specifica per Salesforce
|
||||
var jsonContent = new StringContent(
|
||||
JsonSerializer.Serialize(compositeRequest, SalesforceJsonOptions),
|
||||
System.Text.Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
var response = await _httpClient.PostAsync(compositeUri, jsonContent, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@@ -956,7 +1057,7 @@ namespace DataConnection.REST.Implementations
|
||||
}
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var compositeResponse = JsonSerializer.Deserialize<SalesforceCompositeResponse>(responseContent);
|
||||
var compositeResponse = JsonSerializer.Deserialize<SalesforceCompositeResponse>(responseContent, SalesforceJsonOptions);
|
||||
|
||||
var results = new List<CompositeOperationResult>();
|
||||
|
||||
@@ -975,7 +1076,7 @@ namespace DataConnection.REST.Implementations
|
||||
{
|
||||
if (subResponse.Body is JsonElement bodyElement)
|
||||
{
|
||||
var bodyDict = JsonSerializer.Deserialize<Dictionary<string, object>>(bodyElement.GetRawText());
|
||||
var bodyDict = JsonSerializer.Deserialize<Dictionary<string, object>>(bodyElement.GetRawText(), SalesforceJsonOptions);
|
||||
result.CreatedData = bodyDict;
|
||||
|
||||
// Extract the created ID
|
||||
@@ -1082,18 +1183,28 @@ namespace DataConnection.REST.Implementations
|
||||
var entityId = kvp.Key;
|
||||
var entityData = kvp.Value;
|
||||
|
||||
// Normalizza i valori numerici per evitare problemi con virgole decimali
|
||||
var normalizedData = NormalizeNumericValues(entityData);
|
||||
|
||||
var subrequest = new SalesforceCompositeSubRequest
|
||||
{
|
||||
Method = "PATCH",
|
||||
Url = $"/services/data/v60.0/sobjects/{entityName}/{entityId}",
|
||||
ReferenceId = $"update_{startIndex + index}",
|
||||
Body = entityData
|
||||
Body = normalizedData
|
||||
};
|
||||
compositeRequest.CompositeRequest.Add(subrequest);
|
||||
index++;
|
||||
}
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync(compositeUri, compositeRequest, cancellationToken);
|
||||
// Usa StringContent con configurazione JSON specifica per Salesforce
|
||||
var jsonContent = new StringContent(
|
||||
JsonSerializer.Serialize(compositeRequest, SalesforceJsonOptions),
|
||||
System.Text.Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
var response = await _httpClient.PostAsync(compositeUri, jsonContent, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@@ -1112,7 +1223,7 @@ namespace DataConnection.REST.Implementations
|
||||
}
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var compositeResponse = JsonSerializer.Deserialize<SalesforceCompositeResponse>(responseContent);
|
||||
var compositeResponse = JsonSerializer.Deserialize<SalesforceCompositeResponse>(responseContent, SalesforceJsonOptions);
|
||||
|
||||
var results = new List<CompositeOperationResult>();
|
||||
|
||||
|
||||
@@ -1627,7 +1627,12 @@ public partial class DataCoupler : ComponentBase
|
||||
|
||||
case "edm.decimal":
|
||||
case "edm.double":
|
||||
if (decimal.TryParse(value.ToString(), out decimal decVal))
|
||||
// Usa InvariantCulture per garantire che i decimali usino il punto come separatore
|
||||
if (decimal.TryParse(value.ToString(), System.Globalization.NumberStyles.Number,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out decimal decVal))
|
||||
return decVal;
|
||||
// Fallback: prova con la cultura corrente nel caso il valore usi la virgola
|
||||
if (decimal.TryParse(value.ToString(), out decVal))
|
||||
return decVal;
|
||||
break;
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user