Organizar una lista de objetos implementando una interfaz común, con JaxB


Estoy tratando de ordenar una lista de objetos implementando una interfaz común. Hay 3 clases y 1 interfaz involucrada:

Community class (tiene un método: List getPeople();)

Persona (interfaz tiene un método: String getName();)

Chica clase (implementos Persona)

Niño clase (implementa Persona)

Véase el código a continuación.

Quiero un XML que se vea algo como esto:

<community>
  <people>
    <girl>
      <name>Jane</name>
    </girl>
    <boy>
      <name>John</name>
    </boy>
    <girl>
      <name>Jane</name>
    </girl>
    <boy>
      <name>John</name>
    </boy>
  </people>
</community>

O posiblemente:

<community>
  <people>
   <person>
      <girl>
        <name>Jane</name>
      </girl>
    </person>
    <person>
      <boy>
        <name>John</name>
      </boy>
    </person>
  </people>
</community>

Hasta ahora lo que consigo es esto:

<community>
    <people>
        <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="girl">
            <name>Jane</name>
        </person>
        <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="boy">
            <name>John</name>
        </person>
        <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="girl">
            <name>Jane</name>
        </person>
        <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="boy">
            <name>John</name>
        </person>
    </people>
</community>

Me doy cuenta de que puedo cambiar el elemento a otra cosa, pero quiero que el nombre del elemento sea el nombre especificado en la clase Girl o Boy.

Se Puede hacer esto? Gracias.

@XmlRootElement(name = "community")
public class Community {

 private List<Person> people;

 @XmlElementWrapper
 @XmlElement(name="person")
 public List<Person> getPeople() {
  return people;
 }

 public Community() {
  people = new ArrayList<Person>();
  people.add(new Girl());
  people.add(new Boy());
  people.add(new Girl());
  people.add(new Boy());
 }
}







@XmlRootElement(name = "girl")
public class Girl implements Person {

 @XmlElement
 public String getName() {
  return "Jane";
 }
}


@XmlRootElement(name = "boy")
public class Boy implements Person {

 @XmlElement
 public String getName() {
  return "John";
 }
}



@XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface Person {
 public String getName();
}



public class AnyTypeAdapter extends XmlAdapter<Object, Object> {

 @Override
   public Object marshal(Object v) throws Exception {
    return v;
   }

 @Override
   public Object unmarshal(Object v) throws Exception {
    return v;
   }

}
Author: Andy Thomas, 2010-11-10

2 answers

Para este escenario, recomendaría el uso de @XmlElements. @XmlElements se utiliza para representar el concepto de esquema XML de elección:

Así es como se vería tu ejemplo:

@XmlElements({ 
    @XmlElement(name="girl", type=Girl.class),
    @XmlElement(name="boy", type=Boy.class)
})
@XmlElementWrapper
public List<Person> getPeople() {
    return people;
}

@XmlElementRef corresponde al concepto de grupos de sustitución en el esquema XML. Esta es la razón por la cual la respuesta anterior requiere que la persona sea cambiada de una interfaz a una clase.

 45
Author: Blaise Doughan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-10 15:16:33

OK, si estás preparado para cambiar Persona de una interfaz a una clase base abstracta, entonces estás de oro. Aquí está el código:

public class Main {


  public static void main(String[] args) throws Exception {

    Community community = new Community();

    JAXBContext context = JAXBContext.newInstance(Community.class);
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(community, System.out);

  }
}

@XmlRootElement(name = "community")
@XmlSeeAlso({Person.class})
public class Community {

 private List<Person> people;

 @XmlElementWrapper(name="people")
 @XmlElementRef()
 public List<Person> getPeople() {
  return people;
 }

 public Community() {
  people = new ArrayList<Person>();
  people.add(new Girl());
  people.add(new Boy());
  people.add(new Girl());
  people.add(new Boy());
 }
}

@XmlRootElement(name="boy")
public class Boy extends Person {

 public String getName() {
  return "John";
 }
}

@XmlRootElement(name="girl")
public class Girl extends Person {

 public String getName() {
  return "Jane";
 }
}

@XmlRootElement(name = "person")
@XmlSeeAlso({Girl.class,Boy.class})
public abstract class Person {

  @XmlElement(name="name")
 public abstract String getName();
}

El truco principal fue el uso de @XmlElementRef en la Lista de la Comunidad. Esto identifica el tipo de la clase a través de su @XmlRootElement. También le puede interesar @XmlSeeAlso que ayuda a organizar las declaraciones de contexto.

Y la salida es

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<community>
    <people>
        <girl>
            <name>Jane</name>
        </girl>
        <boy>
            <name>John</name>
        </boy>
        <girl>
            <name>Jane</name>
        </girl>
        <boy>
            <name>John</name>
        </boy>
    </people>
</community>
 12
Author: Gary Rowe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-10 13:59:26