I need to mask the following JSON data :
given JSON:
{
"key1":"value1",
"key2":"value2",
"key3": {
"key4":"value3"
}
}
Maksed data :
{
"key1":"value1",
"key2":"value2",
"key3": {
"key4":"000000"
}
}
we already have a question JSON PII data masking in Java
whoes answer works if the key is part of the main response(for example key1 , key2 or key3) , but i need something for nested values. (using jackson)
What if we have the json property name's that we need to mask but its position can differ in JSON depending on the response received. How to mask such values?
Example : I only know that I need to mask "key4" value.
you already have the answer. Jackson will create a Map for every nested property. You can use the answer from previous question and just change the process of the map
// Process map
if (map.containsKey("key3")) {
Map<String, Object> nestedMap = (Map<String, Object>)map.get("key3");
if (nestedMap.containsKey("key4")) {
nestedMap.put("key4","000000");
}
}
Related
How do I remove some fields with a specified name from a JSON string recursively ?
For example, I want to remove the field "secondName" from the following JSON:
INPUT:
{
"name" : "abc",
"secondName": "qwe",
"add" : "abcd",
"moreDetails" : {
"secondName": "qwe",
"age" : "099"
}
}
OUTPUT:
{
"name" : "abc",
"add" : "abcd",
"moreDetails" : {
"age" : "099"
}
}
I have to remove some fields from a lot of different JSONs with different structures/schema, so I won't be able to deserialize/serialize to/from a POJO.
Gson deserializes any valid Json to LinkedTreeMap, like:
LinkedTreeMap<?,?> ltm = new Gson().fromJson(YOUR_JSON, LinkedTreeMap.class);
Then it is just about making some recursive methods to do the clean up:
public void alterEntry(Entry<?, ?> e) {
if(e.getValue() instanceof Map) {
alterMap((Map<?, ?>) e.getValue());
} else {
if(e.getKey().equals("secondName")) { // hard coded but you see the point
e.setValue(null); // we could remove the whole entry from the map
// but it makes thing more complicated. Setting null
// achieves the same.
}
}
}
public void alterMap(Map<?,?> map) {
map.entrySet().forEach(this::alterEntry);
}
Usage:
alterMap(ltm);
You could try storing the JSON as a JSONObject, iterate over the keys using jsonObject.names() and remove the entries using jsonObject.remove(key).
You can do like below if you know the schema and heirarchy:
JsonObject jsonObj= gson.fromJson(json, JsonObject.class);
jsonObj.getAsJsonObject("moreDetails").remove("secondName");
System.out.println(jsonObj.getAsString());
refer this for more info Remove key from a Json inside a JsonObject
else you need to write a dynamic function which will check each and every element of JSON object and try to find the secondName element in it and remove it.
So consider here as you have multiple nested objects then you need to write a function which will iterate over each element and check its type if its again a jsonObject call the same method recursively or iteratively to check against current element, in each check you need to also verify that the key, if it matches with the key which has to be removed then you can remove it and continue the same.
for a hint on how to check a value type of JSON see this How to check the type of a value from a JSONObject?
I am reading the backup DynamoDB S3 bucket which has format for DynamoDB JSON.
I am trying to convert it into a normal JSON without the AttributeValue.
Original String
{
"id": {
"s": "someString"
},
"name": {
"b": "someByteBuffer"
},
"anotherId": {
"s": "someAnotherString"
},
"version": {
"n": "1"
}
}
Trying to convert to
{
"id": "someString",
"name": "someByteBuffer",
"anotherId": "someAnotherString",
"version": "1"
}
There are many answers which I referred to, but it doesn't convert into normal JSON, it gives me back the same JSON.
Here is what I tried:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(inputJsonString);
final JacksonConverter converter = new JacksonConverterImpl();
Map<String, AttributeValue> map = converter.jsonObjectToMap(jsonNode);
Item item = ItemUtils.toItem(map);
Gson gson = new Gson();
System.out.println(gson.toJson(item.asMap()));
Also, when I was debugging, I wasn't able to create the Map properly. Map would contain key as "id", value as AttributeValue, but the AttributeValue would contain the string inside its own Map<String, AttributeValue> instead of inside String s
I feel I am missing something while creating the Map. Any pointers?
References on Stackoverflow:
Link 1
Link 2
What you're describing as the result sounds correct. If you look at the original JSON string, you have a list of key:value pairs where each value is another list of key:value pairs (length 1, but still a list).
To convert this to a single list of key:value pairs you need to write a map-reduce type of loop. Basically iterate the first map, take the key, then, from the second map (which is the value for the key you just recorded), just take the first entry's value (AttributeValue) and add that key:value pair into a new map (Map<String, AttributeValue>, which you define before the loop). That map you can then convert to JSON and it will result in the JSON string that you were expecting.
Hope that helps!
I have a situation like :
In my project i am using spring boot with spring data.So right now i have to filter my main entity on various fields. Fields and their value are coming in json format in request body in post call like this.
{
"filters": [
{
"field": "ownerUiids",
"terms": "1,2"
},
{
"field": "key",
"terms": "a"
},
{
and many more
}
],
"sortBy": "wwww"
}
I have a pojo class to convert this json into java.and in that pojo class contain list of fields coming from json.
I converted this list into map of field and its value as key and value like :
Map<String,String> filterMap =
request.getFilters().stream().collect(Collectors.toMap(Filter::getField,
Filter::getTerms));
So my problem starts here the name of field in json doesnot match with my column name in db, so while making criteria api the actual column name doesnot appear in query making.
To solve this problem i made constant static map with key as a json field and value is actual column name corresponding to that field like :
public static final Map<String, String> myMap = Collections.unmodifiableMap(
new HashMap<String, String>() {{
put("ownerUiids", "ownerUIID");
put("key", "keyValue");
}});
So when i am making filterMap i need the key should be value of constant myMap i.e instead of Filter::getField if i can do myMap.getKey(Filter::getField). But right now i am not able to do like this.
Map<String,String> filterMap =
request.getFilters().stream().collect(Collectors.toMap(myMap.getKey(Filter::getField),
Filter::getTerms));
After this thing i have my map of column name and its value which is my current req. and also solve my problem.
Please help me in this sol. or if you have better approach than this then that is also appreciable.
You can't use method reference like that. Try this:
Map<String,String> filterMap =
request.getFilters().stream()
.collect(Collectors.toMap(f -> myMap.get(f.getField()),
Filter::getTerms));
for the reason create dto class and format raw data
1 : create dto and write converter method in dto class
List<Dto> dtos = request.getFilters().stream().map(Dto::convert).collect(Collectors.toList())
2 : and now create map
Map<String,String> filterMap = dtos.stream().collect(Collectors.toMap(Dto::getField,Dto::getTerms))
I have come across this question on StackOverflow which asks about converting JSON to Java. The answer shows that another class is modelled to represent the JSON data as well as an object being created and I don't understand why.
Does that object now contain all the information after Gson reads the content or only one key/value pair? If it only contains 1 key/value pair, I'm assuming I would need to create multiple objects for the JSON that I have below which I can the use a loop to iterate over and add the values to a drop down menu?
{
"1": "Annie",
"2": "Olaf",
"3": "Galio",
"4": "TwistedFate",
"5": "XinZhao",
"6": "Urgot",
"7": "Leblanc",
"8": "Vladimir",
"9": "FiddleSticks",
"10": "Kayle",
"11": "MasterYi",
"12": "Alistar",
"13": "Ryze",
"14": "Sion",
"15": "Sivir",
"16": "Soraka",
"17": "Teemo",
"18": "Tristana",
"19": "Warwick",
"20": "Nunu"
}
Essentially what I am aiming to do is:
1) Create a list of names with the Values.
2) Sort the list of names (as it comes unsorted) in alphabetical order
3) Loop through the list and add each name to a drop down menu
4) When a name in the drop down menu is selected, the key associated with that value is passed to another url which receives more data.
Sorry if this is unclear. I've spent a couple of hours trying to understand how to get elements from JSON and display it, as well as trying to create a list where I can use the key to display information the name but have had no luck except for using a for-each loop.
Let's use Jackson's feature that allows you to map any property to a single method (you don't really need a getter here I believe). Just swap the key and value in this universal setter, and add to a TreeMap, which is already sorted by key (name). Then you can output the keys (names) in the alphabetical order and get an ID by name easily.
public static void main(String[] args) throws IOException {
String json = "....."; // your JSON string here
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
ReverseMap pairs = mapper.readValue(json, ReverseMap.class);
for (Map.Entry<Object, String> entry : pairs.getValues().entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
public class ReverseMap {
private TreeMap<Object, String> mapping = new TreeMap<>();
#com.fasterxml.jackson.annotation.JsonAnySetter
public void add(String name, Object value) {
mapping.put(value, name);
}
public Map<Object, String> getValues() {
return mapping;
}
}
Gson Bean Mapping Solution
Okay, what you have is a bit unusual for a JSON object; the keys (the numbers in your case) essentially represent properties of their contained object. That's workable, but you have to understand that, for example, when looking for "Annie" in the JSON object, if you use Gson to map to a "bean" class, which we'll call Data (as in the linked example), then you'd have to create a data object like so:
class Data {
private String _1;
// ...
private String _20;
public String get1() { return _1; }
public void set1(String _1) { this._1 = _1; }
// ...
public String get20() { return _20; }
public void set20(String _20) { this._20 = _20; }
}
And by using Data data = new Gson().fromJson(myJsonString, Data.class); on the given string, you'd be able to find "Annie" by calling... uh... data.get1()?
Clearly, this isn't a good solution.
Better Solutions
Since your data doesn't follow the typical format for a JSON object, you have two options:
If you can, refactor your JSON representation to a more verbose, but better representation for parsing.
Use a different approach to parse the existing JSON.
Solution 1: Changing the JSON representation
Refactoring the JSON would result in an object that (preferably) would look like this:
{
"champions" : [
{
"index" : 1,
"name" : "Annie"
},
{
"index" : 2,
"name" : "Olaf"
},
// ...
]
}
This could map easily to a couple of beans that look like this:
class Data {
private List<Champion> champions;
// TODO getters and setters
}
class Champion {
private int index;
private String name;
// TODO getters and setters
}
However, this adds a lot of unnecessary clutter to the JSON object, and isn't really necessary with only two fields per champion (the name, and their index).
You could simplify that further like so:
{
"champions" : [
"Annie",
"Olaf",
// ...
]
}
The bean class for that would then be:
class Data {
private List<String> champions;
// TODO getters and setters
}
Much simpler, but still requires a change to the JSON you're getting, which in some situations isn't possible. If you used this, though, you could also get rid of the "bean" class entirely, via:
List<String> champions = (List<String>) new Gson().fromJson(myJsonString, new TypeToken<List<String>>(){}.getType());
Solution 2: Changing how the JSON is parsed
The arguably better and cleaner solution is just to change how the JSON is parsed.
The goal here (if I understand you correctly) is to parse the JSON and spit out a collection of strings representing each champion's name, accessible by the numeric index of the champion in the JSON representation.
As such, and because of the way the JSON object is laid out as a simple mapping of strings to strings, we can use Gson to pipe directly into a Map<String, Object>, like so:
Map<String, String> mappedValues = new Gson().fromJson(myJsonString, Map.class);
String anniesName = mappedValues.get("1"); // "Annie"
String olafsName = mappedValues.get("2"); // "Olaf"
boolean hasTwentyOneElements = mappedValues.containsKey("21"); // false
This is shorter, requires no "bean" classes, and keeps the original JSON representation. The downside is that you can't easily tell whether the indices of each entry are correct and consistent; ie. if someone types in the wrong number, or deletes one of the entries.
To get a container of all keys, you just use mappedValues.keySet(), and to get a container of all key-value pairs, you use mappedValues.entrySet(), which gives you a Set<Map.Entry<String, String>>. Both of those can be iterated over, and may be in random order (I'm not sure whether the underlying Map implementation preserves insertion order or not).
To get the index for a given name (ie. champ), you'd use something similar to the following:
String index = null;
for (Map.Entry<String, String> entry : mappedValues.entrySet()) {
if (champ.equals(entry.getValue())) {
index = entry.getKey();
break;
}
}
Of course, you'd have to check to see if index is null after this, and handle that appropriately, but it's easily doable.
EDIT: #vempo's answer provides a cleaner, more efficient lookup strategy by means of inverting the map (although the answer is written for Jackson, instead of Gson); an adaptation of this for Gson is as follows (and yes, there is a vastly superior version in java-8, left out for sake of availability):
public Map<String, String> invertMap(Map<String, String> input) {
Map<String, String> newMap = new LinkedTreeMap<String, String>(); // TODO Pick optimal storage class
for (Map.Entry<String, String> entry : input.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
// ...
Map<String, String> mappedValues = invertMap(new Gson().fromJson(myJsonString, Map.class));
String annieIndex = mappedValues.get("Annie"); // "1"
String olafIndex = mappedValues.get("Olaf"); // "2"
It's worth noting that this sacrifices efficiency of constructing the map by effectively building it twice (once by Gson and once more to invert), but it makes value lookup much more efficient.
I have a json file with same key but different values as follows,
{
"domains" : {
"A" : {
"name" : "a",
"type" : "a1"
},
"B" :{
"name" : "r",
"type" : "g1"
},
"A" : {
"name" : "b",
"type" : "b1"
}
}
}
which is coming from external system.
How to convert the json to java map object and access the different values of the key: A
I am using something like below,
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
which returns a map with unique keys. But I need a map object to hold all the data from json file.
Anyway to achieve this?
I agree with comments by #fge.
But if you really insists on solving this, you could sub-class HashMap (or any other Map), override its put method, and handle duplicates using whatever mechanism you want. Just make sure your Map has a no-arguments constructor.
Guava may also have a datatype that would allow retaining duplicates (Multimap?). If so, you will want to use Jackson's Guava module: https://github.com/FasterXML/jackson-datatype-guava
Answering since a related question was marked duplicate but seems to address the reverse problem where JSON with duplicate keys needs to be created. So that is Java Model -> JSON with duplicate keys.
Simplest way I found was to implement a custom serializer.
public class CustomSerializer extends StdSerializer<ModelToSerialize> {
public CustomSerializer(Class<ModelToSerialize> t) {
super(t);
}
#Override
public void serialize(ModelToSerialize value, com.fasterxml.jackson.core.JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("DuplicateJsonKey", value.getDup1());
gen.writeStringField("DuplicateJsonKey", value.getDup2());
// Map all other values on value that you want to be present in the JSON
gen.writeEndObject();
}
}
And then when you go to serialize:
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(ModelToSerialize.class, new CustomSerializer(ModelToSerialize.class));
objectMapper.registerModule(simpleModule);
String sr = objectMapper.writeValueAsString(modelToSerialize);