XML vers S-Expression

Cette semaine, c'est Nicolas qui vous propose un #KataOfTheWeek : Générateur de S-Séquences

Briefing du Kata : Dans ce challenge viens du concours "Meilleur Dev de France Mai 2014", vous devez convertir un fichier XML au format S-Expression. Les jeux de données en entrée sont constitués de balises ouvrantes et fermantes. Entre la balise ouvrante et la balise fermante d'une paire (même nom), il y a zéro, une ou plusieurs balises et ainsi de suite.

On veut transformer cette notation en utilisant des s-expressions plutôt que des balises, c'est-à-dire en écrivant "(xxx…)" plutôt que "\…\". Dans notre cas… est soit nul, soit correspond à d'autres balises.

Exemple :

  • <access><blockcode><a><blockcode><blockcode></blockcode></blockcode></a></blockcode></access>

Doit être transformé en:

  • (access(blockcode(a(blockcode(blockcode)))))

Le programme doit aussi reconnaître un jeu de données mal formé (pour lequel il y a erreur de correspondance entre balises entrantes et fermantes) et écrire à la place du fichier transformé un message d'erreur standardisé de la forme E n </xxx> </yyy> (chaque terme est séparé par un espace) où :- E est la lettre 'E' (qui signale une erreur)

  • n indique la position en caractères du début de la première balise incorrecte (c'est-à-dire la position de son caractère <)
  • </xxx> correspond à la première balise (fermante) incorrecte
  • </yyy> correspond à la balise (fermante) attendue

Entrée : Une chaîne de caractères contenant une série de balises ouvrantes et fermantes bien ou mal formée.

Sortie : La S-Expression correspondante ou le message d'erreur correspondant.

Saurez-vous résoudre le problème ?

Bon courage !


Et voici une solution proposée par l'auteur :

La solution principale est clairement l'utilisation d'une pile (je viens de me rendre compte que c'est la même chose pour la semaine dernière), il faut juste parcourir la chaine de caractères et verifier les points énoncés.

public static String transformXML(String xml) {
    String retour = null;
    StringBuilder sb = new StringBuilder();
    String[] balises = xml.split("<");
    Stack<String> pile = new Stack<>();

    int n = 0;

    for (int i = 1 ; i < balises.length ; ++i) {
        String s = balises[i];
        if (s.startsWith("/")) {
            if (s.substring(1).equals(pile.peek())) {
                sb.append(')');
                n += s.length() + 1;
                pile.pop();
            } else {
                retour = "E " + n + " <" + s + " </" + pile.pop();
                i = balises.length;
            }
        } else {
            if (s.endsWith(">")) {
                sb.append("(" + s.substring(0, s.length() - 1));
                n += s.length() + 1;
                pile.push(s);
            } else {
                retour = "E " + n + " <" + s + " " + " Not a balise";
                i = balises.length;
            }
        }
    }
    if (retour == null) {
        retour = sb.toString();
    }
    return retour;
}

Votre équipe TakiVeille