Search code examples
javaregexlambdajava-8java-stream

How can I iterate over a delimited String and modify some portions using the Java Stream API?


I want to perform multiple operation on a single String. I need to get a String and extract different sub-strings using a delimiter (";"), then again extract different sub-strings using a delimiter (":"), if the field matched then update the fields and finally join them with original state such that: firstName:Saroj;email:[email protected];eaOldId;city:;center:com.test.Center::0/69 would turn into : firstName:xxxxxx;email:[email protected];eaOldId;city:;center:com.test.Center::0/69

The basic need of the problem is I need to modify some of my required fields (here, ['firstName','email'] to ['xxxxxx','[email protected]']). I want to encrypt the sensitive data.

I have tried the below conventional approach in a main class:

String shortData = "firstName:Saroj;email:[email protected];eaOldId;city:;center:com.test.Center::0/69";
Map<String, List<String>> props = getProperties();
if(props.get("modelClasses").contains("com.test.Member")) {
   List<String> modelFields = props.get(getModelClass());
   List<String> shortDta = Stream.of(shortData.split(";")).map (elem -> new String(elem)).collect(Collectors.toList());
   StringBuilder sb = new StringBuilder();
   for(String data  : shortDta){
      String[] dta = data.split(":");
      if(dta.length>2){
          dta = data.split("=", 2);
      }
      if(dta.length == 1){
         sb.append(data).append(";");
         continue;
      }
      String key = dta[0];
      if(modelFields.stream().anyMatch(d -> d.equalsIgnoreCase(key))){
           String email = props.get("email").toString().replace("[", "").replace("]", "");
           String pattern = props.get("commonPattern").toString().replace("[", "").replace("]", "");
           sb.append(dta[0]).append(":").append(dta[0].equals("email") ? email : pattern).append(";");
      } else {
        sb.append(dta[0]).append(":;");
      }
   }
   if (sb.length() > 0) {
       sb.delete(sb.length() - 1, sb.length());
   }

Below is the data that I'm getting from a properties file (here converting to required object manually)

private Map<String, List<String>> getProperties(){
        Map<String, List<String>> props = new HashMap<>();
        List<String> str1 = new ArrayList<>();
        str1.add("firstName");
        str1.add("email");
        List<String> str2 = new ArrayList<>();
        str2.add("com.test.Member");
        str2.add("com.test.Contact");
        props.put("modelClasses", str2);
        props.put("com.test.Member", str1);
        props.put("com.test.Contact", str1);
        props.put("commonPattern", Arrays.asList("xxxxxxx"));
        props.put("email", Arrays.asList("[email protected]"));
        return props;
 }

If you notice there are multiple : characters present in the String like center:com.test.Center::0/69. I need to take care this also.

Can I do the same thing using Java 8 Streams?


Solution

  • I might just use String#replaceAll here for a regex one line option:

    String input = "firstName:Saroj;email:[email protected];eaOldId;city:;center:com.test.Center::0/69";
    System.out.println(input);
    input = input.replaceAll("(?<=\\bfirstName:)[^;]+", "xxxxxx");
    input = input.replaceAll("(?<=\\bemail:)[^@]+@[^;]+(\\.[^;]+)", "xxxxx@xxx$1");
    System.out.println(input);
    

    This prints:

    firstName:Saroj;email:[email protected];eaOldId;city:;center:com.test.Center::0/69
    firstName:xxxxxx;email:[email protected];eaOldId;city:;center:com.test.Center::0/69
    

    Note: I might recommend completely making the email masked, i.e. use [email protected], to avoid leaking even any information about the user's true email address. By showing things like the domain and/or subdomains, you might make it easier for this information to be discovered.