如何使用排序键将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(); }
解决方案
回答
java.util.Properties基于Hashtable,它不按字母顺序存储其值,而是按每个项目的哈希顺序存储,这就是为什么我们看到自己的行为。
回答
java.util.Properties是java.util.Hashtable的子类。 ("哈希"是这里的关键。)我们必须基于保持/定义订单的东西(例如TreeMap)提出自己的客户实现。
回答
我们可以先对键进行排序,然后循环遍历属性文件中的项目并将其写入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(); } }
回答
我们可以尝试以下方法:
创建一个新类,其功能与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方法。
回答
为什么要首先对XML文件进行排序?大概还有一段代码读取文件并将数据放入另一个Properties对象。我们是否要执行此操作,以便可以手动查找和编辑XML文件中的条目?
回答
最简单的方法是重写keySet。有点hack,并且不能保证可以在将来的实现中使用:
new Properties() { @Override Set<Object> keySet() { return new TreeSet<Object>(super.keySet()); } }
(免责声明:我什至没有测试过它可以编译。)
另外,我们可以使用XSLT之类的格式来重新格式化生成的XML。
回答
这是一种快速而肮脏的方法:
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合同的调用方,因此这是安全的。
- Properties.storeToXML的实现可能会更改,导致其忽略keySet方法。
例如,将来的发行版或者OpenJDK可以使用Hashtable的keys()方法代替keySet。这就是类应该始终记录其"自用"的原因之一(有效的Java项目15)。但是,在这种情况下,发生的最坏情况是输出将恢复为未排序。
- 请记住,属性存储方法将忽略所有"默认"条目。
回答
这是一种为商店Properties.store(OutputStream out,字符串注释)
和`Properties.storeToXML(OutputStream os,字符串注释)产生排序输出的方法:
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);