Подтвердить что ты не робот

Извлечение значений из файла свойств Java по порядку?

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Расширить 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);
    }
}

Ответ 3

Nope - карты по своей сути "неупорядочены".

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

Ответ 4

Решение Dominique Laurent выше отлично работает для меня. Я также добавил следующее переопределение метода:

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

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

    return set;
}

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

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

Ответ 5

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

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

Ответ 6

Apache Commons Configuration может сделать трюк для вас. Я сам не тестировал это, но я проверил их источники и выглядел так, как ключи свойств поддерживаются 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();
    }
}

Ответ 7

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

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

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

Ответ 8

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

Это сохраняется в файле свойств

Ответ 9

Вы должны переопределить также keySet(), если вы хотите экспортировать Свойства как XML:

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

Ответ 10

См. https://github.com/etiennestuder/java-ordered-properties для полной реализации, которая позволяет читать/записывать файлы свойств в определенном порядке.

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

Ответ 11

Я добавлю еще один известный YAEOOJP (еще один пример упорядоченных свойств Java), потому что кажется, что никто не может заботиться о свойствах по умолчанию, которые вы можете подавать в свои свойства.

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

Что мой класс: наверняка не 10 16%, совместимый с любой возможной ситуацией, но это нормально для моих ограниченных тупых целей прямо сейчас. Любые дальнейшие комментарии для коррекции оценены так, чтобы получить Большее благо.

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

Ответ 12

На мой взгляд, 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);
}

Ответ 13

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

IMHO: лучше читать файл строки за строкой (чтобы гарантировать, что заказ гарантирован), чем использовать класс 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, который должен быть закрыт впоследствии (конечно, нет необходимости переписывать его, чтобы взять только файл в качестве аргумента).

Ответ 14

Для тех, кто недавно читал эту тему: просто используйте класс PropertiesConfiguration из org.apache.commons: commons-configuration2. Я проверил, что он сохраняет порядок свойств (потому что он использует LinkedHashMap внутри). Выполнение:

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

удаляет только конечные пробелы и ненужные экранированные символы.

Ответ 15

Альтернативой является просто написать собственный файл свойств с помощью LinkedHashMap, вот что я использую:

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;

public class OrderedProperties {

    private static Map<String, String> properties = new LinkedHashMap<String, String>();

    private static OrderedProperties instance = null;

    private OrderedProperties() {

    }

    //The propertyFileName is read from the classpath and should be of format : key=value
    public static synchronized OrderedProperties getInstance(String propertyFileName) {
        if (instance == null) {
            instance = new OrderedProperties();
            readPropertiesFile(propertyFileName);
        }
        return instance;
    }

    private static void readPropertiesFile(String propertyFileName){
        LineIterator lineIterator = null;
        try {

            //read file from classpath
            URL url = instance.getClass().getResource(propertyFileName);

            lineIterator = FileUtils.lineIterator(new File(url.getFile()), "UTF-8");
            while (lineIterator.hasNext()) {
                String line = lineIterator.nextLine();

                //Continue to parse if there are blank lines (prevents IndesOutOfBoundsException)
                if (!line.trim().isEmpty()) {
                    List<String> keyValuesPairs = Arrays.asList(line.split("="));
                    properties.put(keyValuesPairs.get(0) , keyValuesPairs.get(1));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            lineIterator.close();
        }
    }

    public Map<String, String> getProperties() {
        return OrderedProperties.properties;
    }

    public String getProperty(String key) {
        return OrderedProperties.properties.get(key);
    }

}

Для использования:

    OrderedProperties o = OrderedProperties.getInstance("/project.properties");
    System.out.println(o.getProperty("test"));

Файл свойств образца (в данном случае project.properties):

test=test2