2008-09-10 11 views
12

Me gustaría almacenar un archivo de propiedades como XML. ¿Hay alguna manera de ordenar las claves al hacer esto para que el archivo XML generado esté en orden alfabético?Cómo escribir java.util.Properties en XML con claves ordenadas?

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

Respuesta

26

Aquí está una manera rápida y sucia para hacerlo:

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

Estas son las advertencias:

  • Las Propiedades TMP (un subclase anónima) no cumple con el contrato de Propiedades .

Por ejemplo, si obtuvo keySet y trató de eliminar un elemento, se generará una excepción. Por lo tanto, no permita que las instancias de esta subclase escapen! En el fragmento anterior, nunca lo pasa a otro objeto ni lo devuelve a un llamante que tiene una expectativa legítima de que cumple el contrato de Propiedades, por lo que es seguro.

  • La implementación de Propiedades.storeToXML podría cambiar, haciendo que ignore el método keySet .

Por ejemplo, una versión futura, o OpenJDK, podría usar el método de Hashtable en lugar de keySetkeys(). Esta es una de las razones por las cuales las clases siempre deben documentar su "auto-uso" (Artículo efectivo de Java 15). Sin embargo, en este caso, lo peor que sucedería es que su salida revertiría a no ordenada.

  • Recuerde que las propiedades de almacenamiento métodos ignoran los "por defecto" entradas.
+1

Solo para su información, esto funciona bien con el método "storeToXML", pero no tiene ningún efecto sobre el método "store" anterior (Java 1.6). – Vincent

+0

Muy agradable. Eso es exactamente lo que estaba buscando. Y es mucho más elegante que lo que quería hacer ...;) – brimborium

+0

@Vincent si quiere tener ordenadas las llamadas 'store', necesita anular' keys() 'en vez de' keySet() 'para que necesite seguir la respuesta de @Espen para eso. –

1

java.util.Properties se basa en la tabla hash, que no almacena sus valores en orden alfabético, pero con el fin del hash de cada elemento, es por eso que está viendo el comportamiento que eres.

+0

jaja ... que debe haber golpeado el botón de enviar dentro de un segundo de diferencia! –

1

java.util.Properties es una subclase de java.util. Hashtable. ('Hash', que es la clave aquí.) Tendría que idear su propia implementación de cliente basada en algo que mantiene/define el orden ... como un TreeMap.

2

Primero puede ordenar las claves, luego recorrer los elementos en el archivo de propiedades y escribirlos en el archivo 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(); 
     } 
    } 
0

Usted podría intentar esto:

hacer una nueva clase que hace lo que hace java.util.XMLUtils pero en el cambio de método de guardar esto:

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

a

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

Extienda las propiedades y anule el método de clase de clase storeToXML para llamar al método guardar de su nueva clase.

-2

¿Por qué quiere que se ordene el archivo XML en primer lugar? Presumiblemente, hay otra pieza de código que lee el archivo y coloca los datos en otro objeto Properties. ¿Desea hacer esto para que pueda encontrar y editar entradas manualmente en el archivo XML?

+0

a juzgar por la marca de tiempo en mi p ¡es posible que esto sea anterior a la función de comentarios de StackOverflow! –

3

El truco más simple sería anular KeySet. Un poco de un truco, y no está garantizada para trabajar en futuras implementaciones:

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

(Negación: Ni siquiera he probado que compila.)

Como alternativa, puede usar algo como XSLT para reformatear el producido XML.

20

Aquí está una manera de producir una salida ordenada, tanto para almacenar y 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); 
1

puede implementar su LinkedProperties que se clasifica en lugar de utilizar el Properties de Java.

El ejemplo de código fuente:

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

} 
+0

este es el truco, no sé por qué el almacenamiento xml no funciona para las soluciones anteriores ... –

+0

usando un TreeMap en lugar de LinkedHashMap mantiene las claves ordenadas. –

0

Aquí hay otra solución:

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

En mis pruebas, las demás respuestas a esta pregunta no funcionan correctamente en AIX. Mi máquina de prueba en particular se está ejecutando esta versión:

IBM J9 VM (build 2.4, 1.6.0 JRE de IBM J9 2.4 AIX ppc64-64 jvmap6460sr9-20110624_85526

Después de mirar a través de la aplicación de la store método, encontré que se basa en entrySet. Este método funciona bien para mí.

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