web-dev-qa-db-fra.com

Extraire des valeurs d'un fichier de propriétés Java dans l'ordre?

J'ai un fichier de propriétés où l'ordre des valeurs est important. Je veux pouvoir parcourir le fichier de propriétés et afficher les valeurs en fonction de l'ordre du fichier d'origine.

Cependant, puisque le fichier de propriétés est soutenu par, corrigez-moi si je me trompe, une carte qui ne maintient pas l'ordre d'insertion, l'itérateur renvoie les valeurs dans le mauvais ordre.

Voici le code que j'utilise

Enumeration names = propfile.propertyNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    //do stuff
}

Est-il possible de récupérer les propriétés afin d'écrire mon propre analyseur de fichiers personnalisé?

30
James McMahon

Si vous pouvez modifier les noms de propriété, vous pouvez les préfixer avec un chiffre ou un autre préfixe triable, puis trier le jeu de clés des propriétés.

6
Clint

Étendez Java.util.Properties, Remplacez put() et keys():

import Java.util.Collections;
import Java.util.Enumeration;
import Java.util.HashSet;
import Java.util.LinkedHashSet;
import Java.util.Properties;
import Java.util.HashMap;

public class LinkedProperties extends Properties {
    private final HashSet<Object> keys = new LinkedHashSet<Object>();

    public LinkedProperties() {
    }

    public Iterable<Object> orderedKeys() {
        return Collections.list(keys());
    }

    public Enumeration<Object> keys() {
        return Collections.<Object>enumeration(keys);
    }

    public Object put(Object key, Object value) {
        keys.add(key);
        return super.put(key, value);
    }
}
66

Non - les cartes sont intrinsèquement "non ordonnées".

Vous pouvez éventuellement créer votre propre sous-classe de Properties qui supplante setProperty et éventuellement put, mais cela deviendrait probablement très spécifique à l'implémentation ... Properties est un excellent exemple de mauvaise encapsulation. La dernière fois que j'ai écrit une version étendue (il y a environ 10 ans!), Elle a fini par être hideuse et définitivement sensible aux détails d'implémentation de Properties.

11
Jon Skeet

La solution de Dominique Laurent ci-dessus fonctionne très bien pour moi. J'ai également ajouté le remplacement de méthode suivant:

public Set<String> stringPropertyNames() {
    Set<String> set = new LinkedHashSet<String>();

    for (Object key : this.keys) {
        set.add((String)key);
    }

    return set;
}

Probablement pas le plus efficace, mais il n'est exécuté qu'une seule fois dans mon cycle de vie de servlet.

Merci Dominique!

6
Noah

Exemple de travail:

Map<String,String> properties = getOrderedProperties(new FileInputStream(new File("./a.properties")));
properties.entrySet().forEach(System.out::println);

Code pour cela

public Map<String, String> getOrderedProperties(InputStream in) throws IOException{
    Map<String, String> mp = new LinkedHashMap<>();
    (new Properties(){
        public synchronized Object put(Object key, Object value) {
            return mp.put((String) key, (String) value);
        }
    }).load(in);
    return mp;
}
5

Configuration Apache Commons pourrait faire l'affaire pour vous. Je n'ai pas testé cela moi-même, mais j'ai vérifié leurs sources et il semble que les clés de propriété soient soutenues par LinkedList dans la classe AbstractFileConfiguration:

public Iterator getKeys()
{
    reload();
    List keyList = new LinkedList();
    enterNoReload();
    try
    {
        for (Iterator it = super.getKeys(); it.hasNext();)
        {
            keyList.add(it.next());
        }

        return keyList.iterator();
    }
    finally
    {
        exitNoReload();
    }
}
4
serg

Dans un souci d'exhaustivité ...

public class LinkedProperties extends Properties {

    private final LinkedHashSet<Object> keys = new LinkedHashSet<Object>();

    @Override
    public Enumeration<?> propertyNames() {
        return Collections.enumeration(keys);
    }

    @Override
    public synchronized Enumeration<Object> elements() {
        return Collections.enumeration(keys);
    }

    public Enumeration<Object> keys() {
        return Collections.enumeration(keys);
    }

    public Object put(Object key, Object value) {
        keys.add(key);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        keys.remove(key);
        return super.remove(key);
    }

    @Override
    public synchronized void clear() {
        keys.clear();
        super.clear();
    }
}

Je ne pense pas que les méthodes renvoyant l'ensemble doivent être remplacées car un ensemble par définition ne maintient pas l'ordre d'insertion

3
qwerty
Map<String, String> mapFile = new LinkedHashMap<String, String>();
ResourceBundle bundle = ResourceBundle.getBundle(fileName);
TreeSet<String> keySet = new TreeSet<String>(bundle.keySet());
for(String key : keySet){
    System.out.println(key+" "+bundle.getString(key));
    mapFile.put(key, bundle.getString(key));
}

Cela persiste l'ordre du fichier de propriétés

3
Oshin Talreja

Comme je le vois, Properties est trop lié à Hashtable. Je suggère de le lire pour un LinkedHashMap. Pour cela, vous n'aurez besoin de remplacer qu'une seule méthode, Object put(Object key, Object value), sans tenir compte le Properties en tant que conteneur de clé/valeur:

public class InOrderPropertiesLoader<T extends Map<String, String>> {

    private final T map;

    private final Properties properties = new Properties() {
        public Object put(Object key, Object value) {
            map.put((String) key, (String) value);
            return null;
        }

    };

    public InOrderPropertiesLoader(T map) {
        this.map = map;
    }

    public synchronized T load(InputStream inStream) throws IOException {
        properties.load(inStream);

        return map;
    }
}

Usage:

LinkedHashMap<String, String> props = new LinkedHashMap<>();
try (InputStream inputStream = new FileInputStream(file)) {
    new InOrderPropertiesLoader<>(props).load(inputStream);
}
2
AlikElzin-kilaka

Dans les réponses some, on suppose que les propriétés lues à partir du fichier sont mises à l'instance de Properties (par des appels à put) afin qu'elles apparaissent dans le fichier. Bien que ce soit en général la façon dont il se comporte, je ne vois aucune garantie pour une telle commande.

À mon humble avis, il est préférable de lire le fichier ligne par ligne (afin que l'ordre soit garanti), plutôt que d'utiliser la classe Properties comme analyseur de ligne de propriété unique et de le stocker finalement dans une collection ordonnée comme LinkedHashMap.

Cela peut être réalisé comme ceci:

private LinkedHashMap<String, String> readPropertiesInOrderFrom(InputStream propertiesFileInputStream)
                                                           throws IOException {
    if (propertiesFileInputStream == null) {
      return new LinkedHashMap(0);
    }

    LinkedHashMap<String, String> orderedProperties = new LinkedHashMap<String, String>();

    final Properties properties = new Properties(); // use only as a parser
    final BufferedReader reader = new BufferedReader(new InputStreamReader(propertiesFileInputStream));

    String rawLine = reader.readLine();

    while (rawLine != null) {
      final ByteArrayInputStream lineStream = new ByteArrayInputStream(rawLine.getBytes("ISO-8859-1"));
      properties.load(lineStream); // load only one line, so there is no problem with mixing the order in which "put" method is called


      final Enumeration<?> propertyNames = properties.<String>propertyNames();

      if (propertyNames.hasMoreElements()) { // need to check because there can be empty or not parsable line for example

        final String parsedKey = (String) propertyNames.nextElement();
        final String parsedValue = properties.getProperty(parsedKey);

        orderedProperties.put(parsedKey, parsedValue);
        properties.clear(); // make sure next iteration of while loop does not access current property
      }

      rawLine = reader.readLine();
    }

    return orderedProperties;

  }

Notez simplement que la méthode publiée ci-dessus prend un InputStream qui devrait être fermé par la suite (bien sûr, il n'y a aucun problème à le réécrire pour ne prendre qu'un fichier comme argument).

2
walkeros

Voir https://github.com/etiennestuder/Java-ordered-properties pour une implémentation complète qui permet de lire/écrire des fichiers de propriétés dans un ordre bien défini.

OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
2
Etienne Studer

Vous devez également remplacer keySet () si vous souhaitez exporter les propriétés au format XML:

public Set<Object> keySet() { return keys; }

2
KonstantinSpirov

Je vais ajouter un autre YAEOOJP célèbre ( Encore un autre exemple de commandes Java Propriétés) à ce fil car il semble que personne ne puisse jamais se soucier moins de propriétés par défaut que vous pouvez ajouter à vos propriétés.

@see http://docs.Oracle.com/javase/tutorial/essential/environment/properties.html

C'est ma classe: sûrement pas 1016% conforme à toute situation possible, mais c'est très bien pour mes objectifs stupides limités en ce moment. Tout autre commentaire de correction est apprécié afin que le plus grand bien puisse en bénéficier.

import Java.util.Collections;
import Java.util.Enumeration;
import Java.util.LinkedHashSet;
import Java.util.Map;
import Java.util.Properties;
import Java.util.Set;

/**
 * Remember javadocs  >:o
 */
public class LinkedProperties extends Properties {

    protected LinkedProperties linkedDefaults;
    protected Set<Object> linkedKeys = new LinkedHashSet<>();

    public LinkedProperties() { super(); }

    public LinkedProperties(LinkedProperties defaultProps) {
        super(defaultProps); // super.defaults = defaultProps;
        this.linkedDefaults = defaultProps;
    }

    @Override
    public synchronized Enumeration<?> propertyNames() {
        return keys();
    }

    @Override
    public Enumeration<Object> keys() {
        Set<Object> allKeys = new LinkedHashSet<>();
        if (null != defaults) {
            allKeys.addAll(linkedDefaults.linkedKeys);
        }
        allKeys.addAll(this.linkedKeys);
        return Collections.enumeration(allKeys);
    }

    @Override
    public synchronized Object put(Object key, Object value) {
        linkedKeys.add(key);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(Object key) {
        linkedKeys.remove(key);
        return super.remove(key);
    }

    @Override
    public synchronized void putAll(Map<?, ?> values) {
        for (Object key : values.keySet()) {
            linkedKeys.add(key);
        }
        super.putAll(values);
    }

    @Override
    public synchronized void clear() {
        super.clear();
        linkedKeys.clear();
    }

    private static final long serialVersionUID = 0xC00L;
}
2
Campa

Pour ceux qui ont lu cette rubrique récemment: utilisez simplement la classe PropertiesConfiguration d'org.Apache.commons: commons-configuration2. J'ai testé qu'il maintient l'ordre des propriétés (car il utilise LinkedHashMap en interne). Faire:

`
    PropertiesConfiguration properties = new PropertiesConfiguration();
    properties.read(new FileReader("/some/path));
    properties.write(new FileWriter("/some/other/path"));
`

supprime uniquement les espaces de fin et les échappements inutiles.

0
pootek