Parsing de XML en Java : les élements DOM et la génération en String

J’ai eu à faire un parsing de XML en Java, avec sortie en objet PoJo (un objet Java contenant juste les champs, setters et getters). Puis j’ai eu à faire l’inverse. Parce que la recherche de classes permettant la création d’objets DOM en Java m’a été laborieuse et que je n’ai pas forcément envie d’y repasser beaucoup de temps la prochaine fois, je mets ici un exemple de parseur XML vers Java et Java vers XML. Notez qu’il y a certainement de meilleures solutions, des meilleurs frameworks (et vous avez le droit de les proposer en commentaire). Le but ici est de parser un XML contenant une configuration de murs (PIERRE ou BOIS) dans un jeu vidéo, pour le mode ESCARMOUCHE.

murs bois

Pour expliquer le XML, voici ses caractéristiques :

  • Une zone contient des types de murs
  • Un mur est autorisé ou pas (pas de murs en pierre dans l’eau par exemple)
  • Un mur a une valeur de résistance.
  • Pas besoin de donner la valeur de résistance du mur si on ne l’autorise pas.
<buildings>
 <is_escarmouche>1</is_escarmouche>
 <configuration>
  <zone>
   <zone_name>FORET</zone_name>
   <type>
    <name>MUR PIERRE</name>
    <authorized>1</authorized>
    <resistance>56</resistance>
   </type>
   
   <type>
    <name>MUR BOIS</name>
    <authorized>1</authorized>
    <resistance>56</resistance>
   </type>
  </zone>
 
  <zone>
   <zone_name>MER</zone_name>

   <type>
    <name>MUR PIERRE</name>
    <authorized>0</authorized>
   </type>
   
   <type>
    <name>MUR BOIS</name>
    <authorized>1</authorized>
    <resistance>21</resistance>
   </type>
  </zone>
 </configuration>
 </buildings>

Et le code du parsing XML -> Pojo ainsi que Pojo vers XML. Le but n’est pas de s’attarder sur le remplissage du Pojo, que l’on peut faire comme on veut. Ce que je veux surtout garder, c’est les différentes méthodes à appeler pour générer l’objet DOM ainsi que « sortir » le XML en une String (et pas un Stream) sur la deuxième méthode.

package monpackage;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class Serializer {
   
    
    /**
     * Parse la string xml passée en paramètre
     * Lance une exception quand une erreur survient
     * @param xmlConfiguration
     */
    @Override
    public BuildingPojo parseXmlContent(String xmlConfiguration) throws MalformedXmlException, MalformedXmlContentException {
        try {
            Document document = createDocumentFromString(xmlConfiguration);
            NodeList nodes = document.getDocumentElement().getChildNodes();
            BuildingPojo monPojo = new BuildingPojo();
            
            for (int i=0; i<nodes.getLength(); ++i) {
                Node node = nodes.item(i);
                // Balise IS_ESCARMOUCHE
                if (BuildingPojo.IS_ESCARMOUCHE.equals(node.getNodeName())) {
                    if ("FALSE".equals(getTextContent(node).toUpperCase()))
                        monPojo.setIsEscarmouche(false);
                    else if ("TRUE".equals(getTextContent(node).toUpperCase())) {
                            monPojo.setIsEscarmouche(true);
                    }
                    else
                        throw new MalformedXmlContentException();
                }

                // Balise CONFIGURATION
                if (BuildingPojo.CONFIGURATION.equals(node.getNodeName())) {
                    
                    NodeList BuidingZones = node.getChildNodes();
                    Map<BuildingZoneCodeEnum, List<EscarmoucheBuildingConfiguration>> EscarmoucheCustomBuildingConfiguration = new LinkedHashMap<BuildingZoneCodeEnum, List<EscarmoucheBuildingConfiguration>>();
                    
                    for (int j=0; j<BuidingZones.getLength(); ++j) {
                        Node zoneNode = BuidingZones.item(j);
                        NodeList BuildingTypes = zoneNode.getChildNodes();
                        
                        List<EscarmoucheBuildingConfiguration> escarmoucheBuildingConfigurations = new ArrayList<EscarmoucheBuildingConfiguration>();
                        BuildingZoneCodeEnum zoneCode = null;
                
                        for (int k=0; k<BuildingTypes.getLength(); ++k) {
                            Node BuildingTypeNode = BuildingTypes.item(k);
                            // Balise BUILDING_ZONE_NAME
                            if (BuildingPojo.BUILDING_ZONE_NAME.equals(BuildingTypeNode.getNodeName())) {
                                zoneCode = BuildingZoneCodeEnum.valueOf(getTextContent(BuildingTypeNode).toUpperCase());
                            }
                            // Balise(s) BUILDING_TYPE
                            if (BuildingPojo.BUILDING_TYPE.equals(BuildingTypeNode.getNodeName())) {
                                
                                Element BuildingType = (Element) BuildingTypeNode;
                                
                                BuildingTypeEnum type = BuildingTypeEnum.valueOf(getTextContent(BuildingType.getElementsByTagName(BuildingPojo.BUILDING_TYPE_NAME).item(0)).toUpperCase());                             
                                
                                Status authorization = Status.valueOf(getTextContent(BuildingType.getElementsByTagName(BuildingPojo.AUTHORIZATION).item(0)).toUpperCase());
                    
                                escarmoucheBuildingConfigurations.add(new EscarmoucheBuildingConfiguration(authorization, type));
                            }
                        }
                        // Si aucun Building_type n'a été renseigné
                        if (EscarmoucheBuildingConfigurations.isEmpty())
                            throw new MalformedXmlContentException();
                        
                        escarmoucheCustomBuildingConfiguration.put(zoneCode, EscarmoucheBuildingConfigurations);
                    }
                    // Si aucune zone n'a été renseignée
                    if (EscarmoucheCustomBuildingConfiguration.isEmpty())
                        throw new MalformedXmlContentException();
                    
                    monPojo.setEscarmoucheCustomBuildingConfiguration(escarmoucheCustomBuildingConfiguration);
                }
            }
            return monPojo;
        }
        // Potentiellement lancé par "parseDouble" et "parseLong"
        catch (NumberFormatException e)    { throw new MalformedXmlContentException(); }
        // Potentiellement lancés par "valueOf" provenant des enums
        catch (NullPointerException e)     { throw new MalformedXmlContentException(); }
        catch (IllegalArgumentException e) { throw new MalformedXmlContentException(); }
    }

	private String getTextContent(Node node) {
		return node.getFirstChild().getNodeValue();
	}
    
    
    /**
     * Renvoie un XML à partir du Pojo passé en paramètre
     * @param BuildingPojo
     * @return
     */
    @Override
    public String getXmlContentFromPojo(BuildingPojo BuildingDto) {
        try{
            DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();

            // root elements
            Document doc = documentBuilder.newDocument();
            Element rootElement = doc.createElement(BuildingPojo.BUILDING);

            // IS_ESCARMOUCHE elements
            Element isEscarmouche = createAndFillElement(doc, BuildingPojo.IS_ESCARMOUCHE, BuildingDto.isEscarmouche());
            rootElement.appendChild(isEscarmouche);

            // Zone elements
            Element zones = doc.createElement(BuildingPojo.CONFIGURATION);

            if(BUILDINGDto.getEscarmoucheCustomBuildingConfiguration() != null && ! BUILDINGDto.getEscarmoucheCustomBuildingConfiguration().isEmpty()) {

                for(Map.Entry<BuildingZoneCodeEnum, List<EscarmoucheBuildingConfiguration>> entry : BUILDINGDto.getEscarmoucheCustomBuildingConfiguration().entrySet()) {
                    // Zone elements
                    Element zone = doc.createElement(BuildingPojo.BUILDING_ZONE);

                    Element zoneName = createAndFillElement(doc, BuildingPojo.BUILDING_ZONE_NAME, entry.getKey().name());
                    zone.appendChild(zoneName);

                    for(EscarmoucheBuildingConfiguration advertBUILDINGConf : entry.getValue()) {
                        Element BuildingType = doc.createElement(BuildingPojo.BUILDING_TYPE);

                        Element BuildingTypeName = createAndFillElement(doc, BuildingPojo.BUILDING_TYPE_NAME, advertBUILDINGConf.getBuildingType().name());
                        BuildingType.appendChild(BuildingTypeName);

                        Element authorization = createAndFillElement(doc, BuildingPojo.AUTHORIZATION, advertBUILDINGConf.getAuthorization());
                        BuildingType.appendChild(authorization);

                        zone.appendChild(BuildingType);
                    }
                    zones.appendChild(zone);
                }
            }
            rootElement.appendChild(zones);
            
            doc.appendChild(rootElement);
            
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
    		Transformer transformer = transformerFactory.newTransformer();
    		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            DOMSource source = new DOMSource(doc);
            StringWriter writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            transformer.transform(source, result);
            return writer.toString();
            
        } catch (DOMException e) {
            throw new RuntimeException("Problem while creating XML from CustomBUILDING JSON : " + e.getMessage());
        } catch (ParserConfigurationException e) {
            throw new RuntimeException("Problem while creating Parser Document Builder : " + e.getMessage());
        } catch (TransformerConfigurationException e) {
        	throw new RuntimeException("Problem while creating XML Transformer: " + e.getMessage());
		} catch (TransformerException e) {
        	throw new RuntimeException("Problem while transforming XML : " + e.getMessage());
		}
    }

    
    // Méthodes privées
    //----------------------------------------------------------------------
    
    /**
     * Créé un élément XML et l'ajoute sur le document/élément XML passé en paramètre
     */
    private Element createAndFillElement(Document doc, String key, Object value) {
        Element newElement = doc.createElement(key);
        if(value != null){
            newElement.appendChild(doc.createTextNode(String.valueOf(value)));
        }
        return newElement;
    }
    
    /**
     * Créer un objet xml Document qui permet de parcourir les données xml.
     * @param fragment sont les données xml.
     * @throws Exception 
     */
    private Document createDocumentFromString(String xmlConfiguration) throws MalformedXmlException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlConfiguration)));
        }
        catch (ParserConfigurationException pce) {
            throw new MalformedXmlException();
        }
        catch (SAXException saxe) {
            throw new MalformedXmlException();
        }
        catch (IOException ioe) {
            throw new MalformedXmlException();
        }
    }
    
    // Utilitaires
    //----------------------------------------------------------------------
    
    static enum IfNull {ERROR, SKIP};
    
    
    private String getString(JSONObject json, String key) throws JSONException {
        String s = json.getString(key);
        return checkValue(key, s, IfNull.ERROR);
    }
    
    private Double getDouble(JSONObject json, String key) throws JSONException {
        Double d = json.getDouble(key);
        return checkValue(key, d, IfNull.ERROR);
    }
    
    private Long getLong(JSONObject json, String key) throws JSONException {
        Long l = json.getLong(key);
        return checkValue(key, l, IfNull.ERROR);
    }
    
    private Boolean getBoolean(JSONObject json, String key) throws JSONException {
        Boolean b = json.getBoolean(key);
        return checkValue(key, b, IfNull.ERROR);
    }
    
   /**
    * Get the string associated with a key.
    *
    * @param key A key string.
    * @param value
    * @param ifNull
    * @return An Object which is the value.
    * @throws JSONException if the key is not found or Objet is null.
    */
    <T> T checkValue(String key, T value, IfNull ifNull) throws JSONException {
        if(value == null && IfNull.ERROR.equals(ifNull))
            throw new JSONException("Value for key " + key + " is null");
        
        return value; 
    }
    
}

2 réflexions sur « Parsing de XML en Java : les élements DOM et la génération en String »

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.