java - xmljson - camel unmarshal example
cómo usar alguna indirección al desasignar json a Java Bean usando Jersey usando anotaciones jaxb (2)
Estoy intentando deshacerme de algunos json recibidos (del servicio web de Jira restful).
El problema es: un "problema" tiene una propiedad de "resumen" y una lista de campos.
El resumen no está presente como un atributo en el json recibido, sino como un valor del atributo "campos". Insisto en desarticular esta estructura:
@XmlRootElement
class Issue {
String summary;
List<Field> fields;
// getters/setters and lots of other fields
}
Recibido JSON:
{
"expand":"html",
"self":"https://example.com/jira/rest/api/latest/issue/XYZ-1234",
"key":"XYZ-1234",
"fields":
{
"summary":
{
"name":"summary",
"type":"java.lang.String",
"value":"test 1234"
},
"customfield_10080":
{
"name":"Testeur",
"type":"com.atlassian.jira.plugin.system.customfieldtypes:userpicker"
},
"status":
{
"name":"status",
"type":"com.atlassian.jira.issue.status.Status",
"value":
{
"self":"https://example.com/jira/rest/api/latest/status/5",
"name":"Resolved"
}
},
...
},
"transitions":"https://example.com/jira/rest/api/latest/issue/XYZ-1234/transitions"
}
No quiero usar el propio cliente de Jira (demasiadas dependencias que no quiero en mi aplicación).
editar: le pregunté a mi pregunta de otra manera para intentar aclarar: cómo mapear una estructura de frijoles a un esquema diferente con jax-rs
La clase anotada debe ser biyectiva: debe permitir generar la misma entrada de la que no se ha ordenado. Si aún desea usar un gráfico de objetos no biyectivos, puede usar @XmlAnyElement
la siguiente manera:
public class Issue {
@XmlElement(required = true)
protected Fields fields;
public Fields getFields() {
return fields;
}
}
En la entrada que dio, los campos no son una lista, sino un campo (JSON usa [] para delimitar listas):
public class Fields {
@XmlElement(required = true)
protected Summary summary;
@XmlAnyElement
private List<Element> fields;
public List<Element> getFields() {
return fields;
}
public Summary getSummary() {
return summary;
}
}
Para capturar el resumen, deberá definir una clase dedicada. Los campos restantes se agruparán en la lista de elementos de los fields
.
public class Summary {
@XmlAttribute
protected String name;
public String getName() {
return name;
}
}
A continuación, una prueba de unidad con su entrada muestra que todo funciona:
public class JaxbTest {
@Test
public void unmarshal() throws JAXBException, IOException {
URL xmlUrl = Resources.getResource("json.txt");
InputStream stream = Resources.newInputStreamSupplier(xmlUrl).getInput();
Issue issue = parse(stream, Issue.class);
assertEquals("summary", issue.getFields().getSummary().getName());
Element element = issue.getFields().getFields().get(0);
assertEquals("customfield_10080", element.getTagName());
assertEquals("name", element.getFirstChild().getLocalName());
assertEquals("Testeur", element.getFirstChild().getFirstChild().getTextContent());
}
private <T> T parse(InputStream stream, Class<T> clazz) throws JAXBException {
JSONUnmarshaller unmarshaller = JsonContextNatural().createJSONUnmarshaller();
return unmarshaller.unmarshalFromJSON(stream, clazz);
}
private JSONJAXBContext JsonContextNatural() throws JAXBException {
return new JSONJAXBContext(JSONConfiguration.natural().build(), Issue.class);
}
}
Estas pruebas muestran que sin usar clases dedicadas, su código será difícil de leer rápidamente.
Necesitarás esas dependencias maven para ejecutarlo:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>r08</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.6</version>
</dependency>
{
"expand":"html",
"self":"xxx/jira/rest/api/latest/issue/EPC-2731";,
"key":"EPC-2731",
"fields":{
"summary":{
"name":"summary",
"type":"java.lang.String",
"value":"Fwd: commentaires vides dans FicheSousGroupement"
},
"timetracking":{
"name":"timetracking",
"type":"com.atlassian.jira.issue.fields.TimeTrackingSystemField",
"value":{
"timeestimate":0,
"timespent":60
}
},
"issuetype":{
"name":"issuetype",
"type":"com.atlassian.jira.issue.issuetype.IssueType",
"value":{
"self":"xxx/jira/rest/api/latest/issueType/2";,
"name":"Nouvelle fonctionnalité",
"subtask":false
}
},
"customfield_10080":{
"name":"Testeur",
"type":"com.atlassian.jira.plugin.system.customfieldtypes:userpicker"
},