13

我想将属性文件存储为 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();
}
4

11 回答 11

27

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

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()方法 ofHashtable而不是keySet. 这就是为什么类应该总是记录它们的“自用”(有效的 Java 条款 15)的原因之一。但是,在这种情况下,最糟糕的情况是您的输出将恢复为未排序的。

  • 请记住,属性存储方法会忽略任何“默认”条目。
于 2008-09-10T15:41:34.020 回答
20

这是一种为 storeProperties.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);
于 2010-07-15T06:42:49.047 回答
4

最简单的 hack 是覆盖 keySet。有点hack,并且不能保证在未来的实现中工作:

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

(免责声明:我什至没有测试过它是否可以编译。)

或者,您可以使用 XSLT 之类的东西重新格式化生成的 XML。

于 2008-09-10T15:28:06.470 回答
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();
        }
    }
于 2008-09-10T15:06:49.360 回答
2

在我的测试中,这个问题的其他答案在 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);
}
于 2014-04-15T18:56:51.777 回答
1

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

于 2008-09-10T15:03:13.957 回答
1

java.util.Properties 是 java.util 的子类。哈希表。('哈希',是这里的关键。)您必须根据保持/定义顺序的东西提出自己的客户实现......比如 TreeMap。

于 2008-09-10T15:04:15.110 回答
1

您可以实现您LinkedProperties的排序而不是使用PropertiesJava。

源代码示例:

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

}
于 2012-05-10T02:34:24.600 回答
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 方法以调用新类的保存方法。

于 2008-09-10T15:22:16.423 回答
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");
}
于 2013-07-31T23:12:55.640 回答
-2

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

于 2008-09-10T15:27:17.080 回答