我有一个运行良好的 DataWeave 脚本。我在 Apex 类中使用它。再次,效果很好。这是它:
%dw 2.0
input csvData application/csv
input fieldMappings application/json
input objectProperties application/json
var objectName = objectProperties.ObjectName
output application/apex
---
csvData map ((row) ->
(
(
fieldMappings map (fieldMapping) ->
(fieldMapping.target) : if(row[fieldMapping.source] != "") row[fieldMapping.source] else fieldMapping."defaultValue"
)
reduce ($$ ++ $)
) as Object {class: "Requirement__c"}
)
csvData 看起来像:
First Name,Last Name,Title,Height,Color
Jane, Austin, CEO,7,blue
Bob, Smith, COO, 6,red
fieldMappings 看起来像:
[
{
"source": "First Name",
"target": "FirstName"
},
{
"source": "Last Name",
"target": "LastName"
},
{
"source": "Title",
"target": "Title"
},
{
"source": "Height",
"target": "Height__c"
},
{
"source": "Importance",
"target": "Priority__c"
},
{
"source": "Interests",
"target": "Hobbies__c"
}
]
我没有在上面的代码中使用objectProperties,但下面我确实使用了它,它看起来像
{
"ObjectName":"Contact"
}
但我真正想要的是对象类是动态的,如下所示 - 请注意,我用 DataWeave 变量替换了“Requirement__c”。问题是,DataWeave 按字面意思获取世界对象名称...并且我收到错误。我没有复制错误,并且对代码进行了太多更改,我不记得了,但基本上错误类似于“它无法转换 csvData map ((row) -> ... class : objectName”
它似乎忽略了变量 objectName 并将其视为字符串文字
%dw 2.0
input csvData application/csv
input fieldMappings application/json
input objectProperties application/json
var objectName = objectProperties.ObjectName
output application/apex
---
csvData map ((row) ->
(
(
fieldMappings map (fieldMapping) ->
(fieldMapping.target) : if(row[fieldMapping.source] != "") row[fieldMapping.source] else fieldMapping."defaultValue"
)
reduce ($$ ++ $)
) as Object {class: objectName}
)
有什么想法吗?
好吧,所以我放弃了尝试动态使用
as Object {class: objectProperties.ObjectName}
DataWeave 脚本内部
相反,我决定既然我们可以序列化和反序列化 SObject,我就让 Apex 代码处理对象方面。
首先我简单地尝试了
List<SObject> objectList = (List<SObject>)JSON.deserialize(jsonText,List<SObject>.class);
但问题是我收到了有关多态对象的错误。这是因为 DataWeave 的输出如下所示:
[
{
"FirstName": "Jane",
"LastName": " Austin",
"Title": " CEO",
"Height__c": "7",
"Priority__c": null,
"Hobbies__c": null
},
{
"FirstName": "Bob",
"LastName": " Smith",
"Title": " COO",
"Height__c": " 6",
"Priority__c": null,
"Hobbies__c": null
}
]
Salesforce 需要 JSON 中的附加属性来确定 SObject 的类型。 Salesforce 希望 JSON 看起来像:
[
{
"attributes": {
"type": "Contact"
},
"FirstName": "Jane",
"LastName": " Austin",
"Title": " CEO",
"Height__c": "7",
"Priority__c": null,
"Hobbies__c": null
},
{
"attributes": {
"type": "Contact"
},
"FirstName": "Bob",
"LastName": " Smith",
"Title": " COO",
"Height__c": " 6",
"Priority__c": null,
"Hobbies__c": null
}
]
注意对象名称位于“类型”之后
这就是我所做的。我修改了 DataWeave 脚本以简单地输出 JSON(忘记输出应用程序/apex)
%dw 2.0
input csvData application/csv
input fieldMappings application/json
input objectProperties application/json
var applyMapping = (in, mappings) -> (
mappings map (fieldMapping) -> {
(fieldMapping.target) : if(in[fieldMapping.source] != "") in[fieldMapping.source] else fieldMapping."defaultValue"
}
)
var reduceThis = (in) -> (
in
reduce ($$ ++ $)
)
var attributeThis = (in) -> (
{
attributes: {
"type" : objectProperties.ObjectName
}
} ++ in
)
output application/json
---
csvData map ((row) ->
(
attributeThis(reduceThis(applyMapping(row,fieldMappings)))
)
)
这将读取 CSV 的每一行。 它将动态地从 fieldMappings 找出如何将 CSV 列映射到我选择的 JSON 属性(即 Height__c) 由于某种原因,没有reduce(),它会导致每一列都是它自己的JSON对象,但是reduce给了我CSV的每行一个对象 然后使用“attributeThis”函数,我能够将“属性”及其“类型”连接到每个对象上,并且该类型是从 objectProperties 输入中提取的。
这允许 100% 通用 DataWeave 脚本,可以采用任何 CSV 文件并根据 3 个输入文件将其转换为任何 Salesforce 对象: CSV 文件 字段映射文件(json 格式) 对象属性文件(json 格式)
我不知道如何向 DataWeave 提供单个字符串,因此对象属性文件过于复杂
但请记住,这不会直接为您提供 SObject 列表,在 Apex 代码中,您必须反序列化 DataWeave 的输出。
下面是我可以在 Flow 中调用的 Apex 代码。
这允许我在 Flow 中提示输入 CSV 文件,然后将数据插入或更新插入到 Salesforce 中
这是我的 Apex 代码(请注意:这是一个概念证明,代码很粗糙,未注释,并且在准备好进入黄金时段之前还有很长的路要走)
/**
* Created by Caleb Sidel on 12/12/24.
*/
public class CSVData
{
public class FlowInput
{
@InvocableVariable(Label='Content Document Ids' required=true)
public List<String> contentDocumentIds;
@InvocableVariable(Label='Field Mappings' required=true)
public String jsonFieldMappings;
@InvocableVariable(Label='Object Name' required=true)
public String objectName;
}
@InvocableMethod(label='Read Requirements CSV File')
public static List<List<SObject>> readRequirementsCSVFile(List<FlowInput> inputs)
{
List<List<SObject>> resultList = new List<List<SObject>>();
ContentVersion doc = [SELECT Id, VersionData FROM ContentVersion WHERE ContentDocumentId = :inputs[0].contentDocumentIds[0] AND IsLatest = TRUE];
System.debug('inputs[0].objectName = ' + inputs[0].objectName);
Map<String, String> objectPropertiesMap = new Map<String, String>();
objectPropertiesMap.put('ObjectName',inputs[0].objectName);
String jsonObjectProperties = JSON.serialize(objectPropertiesMap);
System.debug('jsonObjectProperties = ' + jsonObjectProperties);
Blob csvFileBody = doc.VersionData;
String csvAsString = csvFileBody.toString();
DataWeave.Script dwscript = new DataWeaveScriptResource.RequirementsFromCSV();
DataWeave.Result dwresult = dwscript.execute(new Map<String, Object>{
'csvData' => csvAsString,
'fieldMappings' => inputs[0].jsonFieldMappings,
'objectProperties' => jsonObjectProperties
});
System.debug('dwresult = ' + dwresult);
System.debug('dwresult.getValue() = ' + dwresult.getValue());
System.debug('dwresult.getValueAsString() = ' + dwresult.getValueAsString());
//So our DataWeave results in a JSON string that represents a list of SObjects
String jsonText = dwresult.getValueAsString();
System.debug(jsonText);
List<SObject> sObjectList = (List<SObject>)JSON.deserialize(jsonText,List<Sobject>.class);
resultList.add(sObjectList);
return resultList;
}
}
综上所述——可能有更优雅的DataWeave脚本,并且可能有动态使用的方法
as Object
直接,但目前我很满意我有东西(任何东西)可以工作。
如果您是 DataWeave 专家,并且您对动态对象有任何想法,那就太棒了!谢谢您,祝您有美好的一天!