Как написать java.util.Properties в XML с отсортированными ключами? - PullRequest
12 голосов
/ 10 сентября 2008

Я хотел бы сохранить файл свойств в формате XML. Есть ли способ сортировки ключей при этом, чтобы сгенерированный файл XML был в алфавитном порядке?

String propFile = "/path/to/file";
Properties props = new Properties();
/*set some properties here*/
try {
    FileOutputStream xmlStream = new FileOutputStream(propFile);
    /*this comes out unsorted*/
    props.storeToXML(xmlStream,"");
} catch (IOException e) {
    e.printStackTrace();
}

Ответы [ 11 ]

26 голосов
/ 10 сентября 2008

Вот быстрый и грязный способ сделать это:

String propFile = "/path/to/file";
Properties props = new Properties();
/*set some properties here*/
Properties tmp = new Properties() {

  @Override
  public Set<Object> keySet()
  {
    return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
  }

};
tmp.putAll(props);
try {
    FileOutputStream xmlStream = new FileOutputStream(propFile);
    /*this comes out SORTED! */
    tmp.storeToXML(xmlStream,"");
} catch (IOException e) {
    e.printStackTrace();
}

Вот предостережения:

  • Свойства tmp (анонимный подкласс) не выполняет договор о недвижимости.

Например, если вы получили keySet и попытались удалить из него элемент, возникнет исключение. Так что не позволяйте экземплярам этого подкласса убегать! В приведенном выше фрагменте вы никогда не передаете его другому объекту или не возвращаете его вызывающей стороне, которая имеет законное ожидание того, что она выполнит контракт свойств, поэтому она безопасна.

  • Реализация Properties.storeToXML может измениться, заставляя его игнорировать набор ключей способ.

Например, будущий выпуск, или OpenJDK, мог бы использовать keys() метод Hashtable вместо keySet. Это одна из причин, почему классы должны всегда документировать свое «самоиспользование» (Эффективный элемент Java 15). Однако в этом случае худшее, что может произойти, - это то, что ваш вывод вернется к несортированному.

  • Помните, что хранилище свойств методы игнорируют любые «по умолчанию» записи.
20 голосов
/ 15 июля 2010

Вот способ получения отсортированного вывода для магазина Properties.store(OutputStream out, String comments) и Properties.storeToXML(OutputStream os, String comment):

Properties props = new Properties() {
    @Override
    public Set<Object> keySet(){
        return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
    }

    @Override
    public synchronized Enumeration<Object> keys() {
        return Collections.enumeration(new TreeSet<Object>(super.keySet()));
    }
};
props.put("B", "Should come second");
props.put("A", "Should come first");
props.storeToXML(new FileOutputStream(new File("sortedProps.xml")), null);
props.store(new FileOutputStream(new File("sortedProps.properties")), null);
3 голосов
/ 10 сентября 2008

Самым простым взломом было бы переопределить набор ключей. Немного хака, и не гарантируется работа в будущих реализациях:

new Properties() {
    @Override Set<Object> keySet() {
        return new TreeSet<Object>(super.keySet());
    }
}

(Отказ от ответственности: я даже не проверял, что он компилируется.)

В качестве альтернативы, вы можете использовать что-то вроде XSLT для переформатирования созданного XML.

3 голосов
/ 10 сентября 2008

Вы можете сначала отсортировать ключи, а затем перебрать элементы в файле свойств и записать их в XML-файл.

public static void main(String[] args){
        String propFile = "/tmp/test2.xml";
        Properties props = new Properties();
        props.setProperty("key", "value");
        props.setProperty("key1", "value1");
        props.setProperty("key2", "value2");
        props.setProperty("key3", "value3");
        props.setProperty("key4", "value4");

        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(propFile));
            List<String> list = new ArrayList<String>();
            for(Object o : props.keySet()){
                list.add((String)o);
            }
            Collections.sort(list);
            out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            out.write("<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">\n");
            out.write("<properties>\n");
            out.write("<comment/>\n");
            for(String s : list){
                out.write("<entry key=\"" + s + "\">" + props.getProperty(s) + "</entry>\n");
            }
            out.write("</properties>\n");
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
1 голос
/ 15 апреля 2014

В моем тестировании другие ответы на этот вопрос не работали должным образом в AIX. Мой конкретный тестовый компьютер работает под управлением этой версии:

IBM J9 VM (сборка 2.4, JRE 1.6.0 IBM J9 2.4 AIX ppc64-64 jvmap6460sr9-20110624_85526

После просмотра реализации метода store я обнаружил, что он опирается на entrySet. Этот метод хорошо работает для меня.

public static void saveSorted(Properties props, FileWriter fw, String comment) throws IOException {
    Properties tmp = new Properties() {
        @Override
        public Set<Object> keySet() {
            return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
        }

        @Override
        public Set<java.util.Map.Entry<Object,Object>> entrySet() {
            TreeSet<java.util.Map.Entry<Object,Object>> tmp = new TreeSet<java.util.Map.Entry<Object,Object>>(new Comparator<java.util.Map.Entry<Object,Object>>() {
                @Override
                public int compare(java.util.Map.Entry<Object, Object> entry1, java.util.Map.Entry<Object, Object> entry2) {
                    String key1 = entry1.getKey().toString();
                    String key2 = entry2.getKey().toString();
                    return key1.compareTo(key2);
                }
            });

            tmp.addAll(super.entrySet());

            return Collections.unmodifiableSet(tmp);
        }

        @Override
        public synchronized Enumeration<Object> keys() {
            return Collections.enumeration(new TreeSet<Object>(super.keySet()));
        }

        @Override
        public Set<String> stringPropertyNames() {
            TreeSet<String> set = new TreeSet<String>();
            for(Object o : keySet()) {
                set.add((String)o);
            }
            return set;
        }
    };

    tmp.putAll(props);
    tmp.store(fw, comment);
}
1 голос
/ 10 мая 2012

Вы можете реализовать свой LinkedProperties, который сортируется вместо использования Properties Java.

Пример исходного кода:

package com.cpviet.training.eclipseplugin;

import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

public class LinkedProperties extends Properties {

    private static final long serialVersionUID = 1L;

    private Map<Object, Object> m_linkMap = new LinkedHashMap<Object, Object>();

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

    @Override
    public synchronized boolean contains(Object value) {
        return m_linkMap.containsValue(value);
    }

    @Override
    public boolean containsValue(Object value) {
        return m_linkMap.containsValue(value);
    }

    @Override
    public synchronized Enumeration<Object> elements() {
        throw new UnsupportedOperationException("Enumerations are so old-school, don't use them, " + "use keySet() or entrySet() instead");
    }

    @Override
    public Set<Entry<Object, Object>> entrySet() {
        return m_linkMap.entrySet();
    }

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

    @Override
    public synchronized boolean containsKey(Object key) {
        return m_linkMap.containsKey(key);
    }

}
1 голос
/ 10 сентября 2008

java.util.Properties является подклассом java.util. Hashtable . («Хеш», являющийся ключевым здесь.) Вам придется придумать собственную реализацию клиента, основанную на чем-то, что поддерживает / определяет порядок ... как TreeMap.

1 голос
/ 10 сентября 2008

java.util.Properties основан на Hashtable, который хранит свои значения не в алфавитном порядке, а в порядке хеширования каждого элемента, поэтому вы видите поведение, которое у вас есть.

0 голосов
/ 01 августа 2013

Вот еще одно решение:

public static void save_sorted(Properties props, String filename) throws Throwable {
    FileOutputStream fos = new FileOutputStream(filename);
    Properties prop_sorted = new Properties() {
        @Override
        public Set<String> stringPropertyNames() {
            TreeSet<String> set = new TreeSet<String>();
            for (Object o : keySet()) {
                set.add((String) o);
            }
            return set;
        }
    };
    prop_sorted.putAll(props);
    prop_sorted.storeToXML(fos, "test xml");
}
0 голосов
/ 10 сентября 2008

Почему вы хотите, чтобы XML-файл был отсортирован в первую очередь? Предположительно, есть еще один фрагмент кода, который читает файл и помещает данные в другой объект Properties. Вы хотите сделать это, чтобы вы могли вручную находить и редактировать записи в файле XML?

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