Como usar recursividade para parsear grupos dentro de grupos?

Estou começando agora com recursividade, logo se alguém puder ajudar serei muito grato!

Eu tenho a String abaixo:

field1: typeA
field2: typeA
field3: typeB
group1:
    fieldA: typeA
    fieldB: typeB
    subGroup1:
        fieldX: typeB
        fieldY: typeB
    fieldC: typeC
field4: typeA
group2:
    fieldA: typeB
    subGroup1:
        fieldX: typeA
        fieldY: typeA
    fieldB: typeA
    fieldC: typeC
field5: typeC
field6: typeD

Note que eu posso ter grupos dentro de grupos dentro de grupos, etc. logo eu acredito que recursividade é obrigatória.

Note que o espaço no início de cada linha determina se o campo pertence ao grupo ou não.

Eu gostaria de ter um método que retorna uma LinkedHashMap<String, Object>, onde o Object pode ser uma String indicando um campo (e não um grupo) ou outra LinkedHashMap<String, Object> indicando um grupo (e não um campo).

Eu tentei mas fracassei :frowning: Estou estudando recursividade para ver se um dia consigo fazer isso.

Para ajudar alguém que possa ajudar, abaixo o código:

	public static void main(String[] args) {
		
		String text = String.join("\n",
				
				"field1: typeA",
				"field2: typeA",
				"field3: typeB",
				"group1:",
				"    fieldA: typeA",
				"    fieldB: typeB",
				"    subGroup1:",
				"        fieldX: typeB",
				"        fieldY: typeB",
				"    fieldC: typeC",
				"field4: typeA",
				"group2:",
				"    fieldA: typeB",
				"    subGroup1:",
				"        fieldX: typeA",
				"        fieldY: typeA",
				"    fieldB: typeA",
				"    fieldC: typeC",
				"field5: typeC",
				"field6: typeD",
				
				"");
		
		Map<String, Object> solution = doIt(text);
		System.out.println(solution);
	}
	
	private static LinkedHashMap<String, Object> doIt(String text) {
		// solution goes here...
		return null;
	}
}

Legal ter colocado esse código inicial @estacioCoder

Pra ficar mais fácil te ajudar, se você fosse montar esse hashmap com a resposta na mão (o objeto solution) do método main… como esse hashmap ficaria?
Cria ele na mão usando java puro,

Fala Abel! Obrigado por tentar ajudar.

O que eu gostaria:

public static void main(String[] args) {
	Map<String, Object> map = new LinkedHashMap<String, Object>();
	
	map.put("field1", "typeA");
	map.put("field2", "typeA");
	map.put("field3", "typeB");
	
	Map<String, Object> map2 = new LinkedHashMap<String, Object>();
	map2.put("fieldA", "typeA");
	map2.put("fieldB", "typeB");
	
	Map<String, Object> map3 = new LinkedHashMap<String, Object>();
	map3.put("fieldX", "typeB");
	map3.put("fieldY", "typeB");
	map2.put("subGroup1", map3);
	
	map2.put("fieldC", "typeC");
	
	map.put("group1", map2);
	
	map.put("field4", "typeA");
	
	Map<String, Object> map4 = new LinkedHashMap<String, Object>();
	map4.put("fieldA", "typeB");
	
	Map<String, Object> map5 = new LinkedHashMap<String, Object>();
	map5.put("fieldX", "typeA");
	map5.put("fieldY", "typeA");
	map4.put("subGroup1", map5);
	
	map4.put("fieldB", "typeA");
	map4.put("fieldC", "typeC");
	
	map.put("group2", map4);
	
	map.put("field5", "typeC");
	map.put("field6", "typeD");
	
	System.out.println(map);
}

O output bate com o original:

{field1=typeA, field2=typeA, field3=typeB, group1={fieldA=typeA, fieldB=typeB, subGroup1={fieldX=typeB, fieldY=typeB}, fieldC=typeC}, field4=typeA, group2={fieldA=typeB, subGroup1={fieldX=typeA, fieldY=typeA}, fieldB=typeA, fieldC=typeC}, field5=typeC, field6=typeD}

Esta string parece estar em um formato compatível com YAML.

Se for para código em produção (aplicações “sérias”, etc), eu não faria o parsing manualmente, pois já existem bibliotecas prontas para isso.

Por exemplo, se você usar o SnakeYAML, basta importar a classe principal:

import org.yaml.snakeyaml.Yaml;

E depois basta fazer:

String text = ... // string contendo o texto
Yaml yaml = new Yaml();
Map<String, Object> dados = yaml.load(text);
System.out.println(dados);

Se for fazer manualmente, vai ter que no mínimo construir uma versão simplificada do parser (já que o formato YAML é mais complexo do que a sua string, pois permite muitas outras variações no formato). Mas mesmo isso não é algo tão simples, veja por exemplo este parser, que de certa forma “funciona”, apesar de não dar o formato que você quer - é apenas para te dar uma ideia da complexidade da tarefa.

O próprio código fonte do SnakeYAML pode te dar uma ideia do quão complexo isso é. Claro que ele é mais completo e contempla todas as possibilidades do formato YAML, mas ainda sim, uma versão simplificada para o seu caso ainda seria complexo demais.

Então eu recomendaria usar uma biblioteca que já tem tudo pronto. A menos, é claro, que seja para fins didáticos ou de “desafio” (que é válido). Aí eu sugiro dar estudar o código dessas bibliotecas prontas, para entender melhor como é feito.

1 curtida