如何使用排序键将java.util.Properties写入XML?

时间:2008-09-10 15:00:49

标签: java 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();
}

11 个答案:

答案 0 :(得分:26)

这是一种快速而肮脏的方法:

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可能会改变, 导致它忽略keySet 方法

例如,未来版本或OpenJDK可以使用keys()的{​​{1}}方法而不是Hashtable。这就是为什么类应该始终记录它们的“自用”(Effective Java Item 15)的原因之一。但是,在这种情况下,最糟糕的情况是输出将恢复为未排序。

  • 请记住属性存储 方法忽略任何“默认” 条目。

答案 1 :(得分:20)

这是为商店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);

答案 2 :(得分:3)

您可以先对键进行排序,然后遍历属性文件中的项目并将其写入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();
        }
    }

答案 3 :(得分:3)

最简单的方法是覆盖keySet。有点破解,并且不能保证在将来的实现中起作用:

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

(免责声明:我甚至没有测试过它的编译。)

或者,您可以使用类似XSLT的内容来重新格式化生成的XML。

答案 4 :(得分:1)

java.util.Properties基于Hashtable,它不按字母顺序存储其值,但是按照每个项目的哈希顺序,这就是您看到自己行为的原因。

答案 5 :(得分:1)

java.util.Properties是java.util。 Hashtable 的子类。 ('Hash',这里的关键。)你必须基于保持/定义顺序的东西来提出你自己的客户实现......就像TreeMap一样。

答案 6 :(得分:1)

您可以实现排序的LinkedProperties,而不是使用Java的Properties

源代码示例:

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

}

答案 7 :(得分:1)

在我的测试中,此问题的其他答案在AIX上无法正常运行。我的特定测试机器正在运行此版本:

  

IBM J9 VM(build 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);
}

答案 8 :(得分:0)

你可以试试这个:

创建一个新的类来执行java.util.XMLUtils所做的操作,但是在save方法中更改了这个:

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

扩展属性并覆盖Properties类storeToXML方法以调用新类的save方法。

答案 9 :(得分:0)

这是另一种解决方案:

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

答案 10 :(得分:-2)

为什么要首先对XML文件进行排序?据推测,还有另一段代码可以读取文件并将数据放入另一个Properties对象中。您是否要这样做,以便您可以手动查找和编辑XML文件中的条目?