如何利用DataWeave多级动态选择器完成请求映射与服务响应的动态字段映射
Got it, let's tackle this problem head-on. Mapping nested data paths (even with array indices) to a flat target structure is totally doable with a two-step approach: parsing the path strings into traversable nodes, then resolving those nodes against the source data.
Core Approach
The key is to break down each path in your mapping rules into individual access steps. For example:
a.b.sourceField1becomes["a", "b", "sourceField1"]a.x[2].sourceFieldXbecomes["a", "x", 2, "sourceFieldX"]
Once you have these node lists, you can traverse the source data step by step to pull out the correct value, then assign it to the corresponding target field.
Step 1: Parse Path Strings into Nodes
First, we need a function to convert those path strings into a list of nodes (strings for object keys, numbers for array indices). A regex works great here to split the path correctly:
function parsePath(path) { // Match dot-separated keys or array indices like [0] const regex = /([^\.\[\]]+)(?:\[(\d+)\])?/g; const nodes = []; let match; while ((match = regex.exec(path)) !== null) { nodes.push(match[1]); // If there's an array index, add it as a number if (match[2]) { nodes.push(Number(match[2])); } } return nodes; }
Testing this function:
parsePath("a.b.sourceField1")returns["a", "b", "sourceField1"]parsePath("a.x[2].sourceFieldX")returns["a", "x", 2, "sourceFieldX"]
Step 2: Resolve Values from Source Data
Next, write a function that takes the source data and the parsed node list, then traverses the data to get the value:
function getValueByPath(source, nodes) { let current = source; for (const node of nodes) { // Check if current is valid (not undefined/null) before accessing if (current === undefined || current === null) { return undefined; // Or handle missing data as needed } current = current[node]; } return current; }
Step 3: Build the Target Structure
Now combine these two functions to process your mapping rules and generate the target object:
function mapResponse(sourceData, mapping) { const target = {}; for (const [targetField, sourcePath] of Object.entries(mapping)) { const nodes = parsePath(sourcePath); const value = getValueByPath(sourceData, nodes); target[targetField] = value; } return target; }
Test with Your Example
Let's test this with your sample data:
// Your mapping rules const mapping = { "targetField1": "a.b.sourceField1", "targetField2": "a.c.sourceField2" }; // Service response const sourceData = { "a": { "b": {"sourceField1": "sourceValue1"}, "c": {"sourceField2": "sourceValue2"} } }; // Generate target structure const result = mapResponse(sourceData, mapping); console.log(result); // Output: { targetField1: "sourceValue1", targetField2: "sourceValue2" }
Test with Array Paths
Let's also test an array scenario to make sure it works:
const arrayMapping = { "targetFieldX": "a.x[2].sourceFieldX" }; const arraySourceData = { "a": { "x": [ {"sourceFieldX": "val0"}, {"sourceFieldX": "val1"}, {"sourceFieldX": "val2"} ] } }; const arrayResult = mapResponse(arraySourceData, arrayMapping); console.log(arrayResult); // Output: { targetFieldX: "val2" }
Edge Cases to Consider
- Missing paths: The
getValueByPathfunction returnsundefinedif any step in the path doesn't exist. You can adjust this to return a default value or throw an error based on your needs. - Non-existent array indices: If you try to access an index that's out of bounds, it will return
undefined(same as JavaScript's behavior for array access). - Nested arrays: This approach works for deeply nested arrays too, like
a.x[1].y[0].value.
内容的提问来源于stack exchange,提问作者Jorge Garcia

