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

Как написать java.util.Properties в XML с отсортированными ключами?

Я хотел бы сохранить файл свойств как 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();
}
4b9b3361

Ответ 1

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

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). Однако в этом случае самое худшее, что может произойти, это то, что ваш вывод вернется к несортированному.

  • Помните, что хранилище свойств методы игнорируют любые "по умолчанию" записей.

Ответ 2

Здесь вы можете создать отсортированный вывод для хранилища 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

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

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

(Отказ от ответственности: я даже не тестировал его компиляцию.)

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

Ответ 4

Сначала вы можете сортировать ключи, а затем перебирать элементы в файле свойств и записывать их в 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();
        }
    }

Ответ 5

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

Ответ 6

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

Ответ 7

Вы можете реализовать свой 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);
    }

}

Ответ 8

В моем тестировании другие ответы на этот вопрос не работают должным образом в 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);
}

Ответ 9

Вы можете попробовать следующее:

Создайте новый класс, который делает то, что делает java.util.XMLUtils, но в методе сохранения это:

Set keys = props.keySet();
Iterator i = keys.iterator();

к

Set keys = props.keySet();
List<String> newKeys = new ArrayList<String>();
for(Object key : keys)
{
   newKeys.add(key.toString());
}
Collections.sort(newKeys);
Iterator i = newKeys.iterator();

Расширьте свойства и переопределите метод класса StoreToXML класса Properties, чтобы вызвать новый метод сохранения класса.

Ответ 10

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

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

Ответ 11

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