Вытащить значения из файла свойств Java по порядку? - PullRequest
30 голосов
/ 21 августа 2009

У меня есть файл свойств, в котором важен порядок значений. Я хочу иметь возможность перебирать файл свойств и выводить значения в соответствии с порядком исходного файла.

Однако, так как файл свойств поддерживается, поправьте меня, если я ошибаюсь, карту, которая не поддерживает порядок вставки, итератор возвращает значения в неправильном порядке .

Вот код, который я использую

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

Есть ли способ вернуть свойства обратно, если бы я не написал свой собственный анализатор файлов?

Ответы [ 14 ]

66 голосов
/ 13 мая 2010

Расширить java.util.Properties, переопределить put() и 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);
    }
}
11 голосов
/ 21 августа 2009

Нет - карты изначально "неупорядочены".

Вы можете возможно создать свой собственный подкласс Properties, который переопределит setProperty и, возможно, put, но он, вероятно, будет очень специфичным для реализации ... Properties является ярким примером плохой инкапсуляции. Когда я последний раз писал расширенную версию (около 10 лет назад!), Она оказалась отвратительной и определенно чувствительной к деталям реализации Properties.

6 голосов
/ 16 февраля 2011

Приведенное выше решение Доминика Лорана прекрасно работает для меня. Я также добавил следующий метод переопределения:

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

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

    return set;
}

Возможно, не самый эффективный, но он выполняется только один раз в моем жизненном цикле сервлета.

Спасибо, Доминик!

6 голосов
/ 21 августа 2009

Если вы можете изменить имена свойств, вы можете добавить к ним префикс числовым или другим сортируемым префиксом, а затем отсортировать набор ключей свойств.

5 голосов
/ 15 декабря 2017

Рабочий пример:

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

Код для этого

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;
}
4 голосов
/ 21 августа 2009

Конфигурация Apache Commons может помочь вам. Я не проверял это сам, но я проверил их источники и похоже, что ключи свойств поддерживаются LinkedList в классе 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();
    }
}
3 голосов
/ 18 апреля 2016
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));
}

Сохраняется порядок файла свойств

3 голосов
/ 14 февраля 2013

В интересах полноты ...

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();
    }
}

Я не думаю, что методы, возвращающие набор, должны быть переопределены, поскольку набор по определению не поддерживает порядок вставки

2 голосов
/ 09 декабря 2016

В некоторых ответах предполагается, что свойства, считанные из файла, помещаются в экземпляр Properties (с помощью вызовов put), чтобы отобразить их в файле. Хотя в целом так и происходит, я не вижу никакой гарантии для такого заказа.

ИМХО: лучше читать файл построчно (чтобы порядок был гарантирован), чем использовать класс Properties просто как парсер одного свойства и, наконец, сохраните его в некоторой упорядоченной коллекции, например LinkedHashMap.

Это может быть достигнуто следующим образом:

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;

  }

Просто обратите внимание, что приведенный выше метод требует InputStream, который впоследствии следует закрыть (конечно, нет проблем переписать его, чтобы в качестве аргумента использовался только файл).

2 голосов
/ 11 августа 2016

На мой взгляд, Properties сильно привязано к Hashtable. Я предлагаю прочитать это для того, чтобы LinkedHashMap. Для этого вам нужно будет переопределить только один метод, Object put(Object key, Object value), , игнорируя Properties в качестве контейнера ключа / значения:

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;
    }
}

Использование:

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