Parsing complex string data I have a complex strings like the following:

$company=&gt;Company(code-&gt;MyCompany)
$brand=&gt;Brand(code-&gt;Brand_1, company-&gt;$company)


and then I prepare objects out of them as follows:

Macro [key=$company, type=Company, map={code=MyCompany}]
Macro [key=$brand, type=Brand, map={ company=$company, code=Brand_1}]


using the following code for parsing. Is there a better way?

public class Macro {

    private String key;

    private String type;

    private Map&lt;String, String&gt; map;
}

public static Macro getMacro(String str) {
    final Macro macro = new Macro();
    final Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();
    final Matcher matcher = Pattern.compile("\\(([^)]+)\\)").matcher(str);
    while (matcher.find()) {
        final List&lt;String&gt; list = Arrays.asList(matcher.group(1).split(",")).stream()
                .collect(Collectors.toList());
        list.stream().forEach(a -&gt;
        {
            final String[] ar = a.split("-&gt;");
            map.put(ar[0], ar[1]);
        });
    }
    str = str.replaceAll("\\(.*\\)", "");
    final String[] s = str.split("=&gt;");
    macro.setMap(map);
    macro.setKey(s[0]);
    macro.setType(s[1]);
    return macro;
}
Pattern.compile("\\(([^)]+)\\)");


The Pattern is always the same so you may just as well make it a private static final constant. This will avoid having to recompile the pattern each time.



Arrays.asList(/*..*/).stream().collect(Collectors.toList());


Why this dance? Arrays.asList returns a List. You only do a stream on the return result.

    final List&lt;String&gt; list = Arrays.asList(matcher.group(1).split(","));
    list.stream().forEach(a -&gt;
    {
        final String[] ar = a.split("-&gt;");
        map.put(ar[0], ar[1]);
    });


However a Pattern an return a Stream split according to the pattern directly:

Pattern COMMA_PATTERN = Pattern.compile("\\s*,\\s*");


COMMA_PATTERN.splitAsStream(matcher.group(3)).forEach(a -&gt;
{
    //...
}




You can use a single match of a Pattern to find both the leading values and the string inside the parenthesis:

private static final Pattern FULL_PATTERN = Pattern.compile("^(\\$\\w+)\\s*=&gt;\\s*(\\w+)\\s*\\(([^)]+)\\)$");
private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("(\\w+)\\s*-&gt;\\s*([\\$\\w]+)");
private static final Pattern COMMA_PATTERN = Pattern.compile("\\s*,\\s*");

public static Macro getMacro(String str) {
    final Macro macro = new Macro();
    final Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();
    final Matcher matcher = FULL_PATTERN.matcher(str);
    if(!matcher.matches()) {
        throw new IllegalArgumentException("...");
    }

    COMMA_PATTERN.splitAsStream(matcher.group(3)).forEach(a -&gt;
    {
        final Matcher kvMatcher = KEY_VALUE_PATTERN.matcher(a);
        if(!kvMatcher.matches()){
             throw new IllegalArgumentException("...");
        }

        map.put(kvMatcher.group(1), kvMatcher.group(2));
    });
    macro.setMap(map);
    macro.setKey(matcher.group(1));
    macro.setType(matcher.group(2));
    return macro;

}


You'll note I added \\s* to various points in each pattern, This lets you ignore the whitespace that may surround the operators.

The \\w in a Pattern means a word character. If you only want alphanumeric +undescore then you'llneed to replace each instance of it with [\\p{Alnum}_]