Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Values for some keys given a map #449

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

Joxebus
Copy link

@Joxebus Joxebus commented Oct 24, 2017

This functionallity adds to JoltUtils the capability to change the value
of one or more nodes given a map where the key match with a node key, and the
value is the new content for that node.key.

Two methods added:

JoltUtils.replaceValues -> Receives a Map with the keys and values to replace in a Jackson Object.

Example:

Given:

{
        "input" : {
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "ReplaceThis"
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",

            "L3_B" : "This should not be replaced"
        },

        "mappingPaths" : {
            "L3_B" : ["L1_A.L2_A.L3_B"]
        },

        "valuesToReplace" : {
            "L3_B" : {
                "ReplaceThis" : "This has been replaced."
            }

        }
}

When:

// java code
Object data = jsonMapObject.get( "input" );
Map<String,Object> mappingPaths = (Map<String, Object>)jsonMapObject.get( "mappingPaths" );
Map<String,Object> valuesToReplace = (Map<String, Object>)testUnit.get( "valuesToReplace" );
            
// data is a Jackson Object
JoltUtils.replaceValues(data, mappingPaths, valuesToReplace);

Then:

{
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "This has been replaced."
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",
            "L3_B" : "This should not be replaced" 
}

This functionallity adds to JoltUtils the capability to change the value
of one or more nodes given a map where the key match with a node key, and the
value is the new content for that node.key.

Two methods added:

JoltUtils.replaceRecursive -> Which receive a Jackson Object a Key and a value to replace
JoltUtils.replaceValues    -> Which receive a Jackson Object and a Map with several Keys and Values to replace.

Example:

Given:

{
        "L1_A": {
                "L2_A": {
                        "L3_A": "Good",
                        "L3_B": "RemoveThis"
                },
                "L2_B": "l2_b"
        },
        "L1_B": "l1_b",

        "L3_B": "RemoveThis"
}

When:

// java code

Map<String, Object> params = new HashMap<>();
params.put("L3_A", String.valueOf(j));
params.put("L3_B", String.valueOf(j));
// data is a Jackson Object
JoltUtils.replaceValues(data, params);

Then:

{
        "L1_A": {
                "L2_A": {
                        "L3_A": "Replaced1",
                        "L3_B": "Replaced2"
                },
                "L2_B": "l2_b"
        },
        "L1_B": "l1_b",

        "L3_B": "Replaced2"
}
*/
public static void replaceValues(Object json, Map<String, Object> params){
for(String key : params.keySet()) {
replaceRecursive(json, key, params.get(key));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loops over the whole input tree for each key. It should ideally only recurse thru the content once.

Could at each level compare the input keyset and the replacementMapKeyset.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now first I look for the parent node and then apply the replacement, please take a look on my updates and tell me if you think something else could be changed.

* @param keyToReplace the key to remove from the document
* @param value the new value for the keyToReplace
*/
public static void replaceRecursive( Object json, String keyToReplace, Object value ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is more of an "updateValueRecursive" or "replaceValueRecursive"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to replaceValueRecursive

@@ -62,6 +62,54 @@ public static void removeRecursive( Object json, String keyToRemove ) {
}

/**
* Replaces a value recursively from anywhere in a JSON document.
* NOTE: mutates its input.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls add a small example in the javadoc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Map<String,Object> toReplace = (Map<String, Object>)testUnit.get( "replace" );
Object expected = testUnit.get( "expected" );

for(String key : toReplace.keySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is clever in that it is using the same test fixture to test two methods, but it would be simpler to understand if this test only changed one key / had a separate test fixture where only one key got replaced/updated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I removed this test and just test the replace value, since now we can give a path for each key to be replaced and a set of values that can be replaced.

Omar Bautista and others added 5 commits November 21, 2017 11:13
Now we can provide the full path where the replacement will be done, and a map to match the values to be replaced.

Given the next example:

{
        "input" : {
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "ReplaceThis"
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",

            "L3_B" : "This not should be replaced"
        },

        "mappingPaths" : {
            "L3_B" : ["L1_A.L2_A.L3_B"]
        },

        "valuesToReplace" : {
            "L3_B" : {
                "ReplaceThis" : "This has been replaced."
            }

        },

        "expected" : {
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "This has been replaced."
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",

            "L3_B" : "This not should be replaced"
        }
    }

The 'input' is the original json, the 'mappingPaths' is a map where each key contains a list of fullpaths (a fullpath is the human reading representation of a node from json object)
the 'values' to replace is a map that contains a map where each key contains a map that represents the { "oldValue" : "newValue" } elements if the old value matches in the key where
then it will be replaced with the new value, if not it will be ignored.
This implementation is added for special cases where we don't know the
exact value to replace but by default we wan't a replacement.

Given the next example:

   {
        "input" : {
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "ReplaceThis"
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",

            "L3_B" : "Replace any content"
        },

        "mappingPaths" : {
            "L3_B" : [
                "L1_A.L2_A.L3_B",
                "L3_B"
            ]
        },

        "valuesToReplace" : {
            "L3_B" : {
                "ReplaceThis" : "This has been replaced.",
                "*" : "The star replace any content"
            }

        },

        "expected" : {
            "L1_A" : {
                "L2_A" : {
                    "L3_A" : "Good",
                    "L3_B" : "This has been replaced."
                },
                "L2_B" : "l2_b"
            },
            "L1_B" : "l1_b",

            "L3_B" : "The star replace any content"
        }
    }

On the main structure at "L3_B" key the "*" means that no mather what is the old value it will be replaced with the text "The star replace any content".
@Joxebus
Copy link
Author

Joxebus commented Nov 21, 2017

@milosimpson I made a few changes to my code and maybe I wasn't able to explain myself clearly in the comments, maybe I can have feedback via Skype or Hangouts if you want, let me know if you have time.

@Joxebus
Copy link
Author

Joxebus commented Nov 14, 2018

Conflicts solved.

@Joxebus Joxebus requested a review from milosimpson November 14, 2019 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants