问题
I'm trying to unmarshall some received json (from Jira restful web service).
Problem is: an "issue" has a "summary" property and a list of fields.
Summary is not present as an attribute in the received json, but as a value of the "fields" attribute. I insist on unmarshalling to this structure:
@XmlRootElement
class Issue {
String summary;
List<Field> fields;
// getters/setters and lots of other fields
}
Received 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"
}
I don't want to use Jira's own client (too many dependencies which I don't want in my app).
edit: I asked my question another way to try to make it clear: how to map a bean structure to a different schema with jax-rs
回答1:
Your annotated class should be bijective: it should allow to generate the same input from which it was unmarshalled. If you still want to use a non-bijective object graph, you can use @XmlAnyElement
the following way:
public class Issue {
@XmlElement(required = true)
protected Fields fields;
public Fields getFields() {
return fields;
}
}
In the input you gave, fields is not a list, but a field (JSON uses [] to delimit lists):
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;
}
}
In order to catch Summary, you'll have to define a dedicated class. Remaining fields will be grouped in the fields
list of elements.
public class Summary {
@XmlAttribute
protected String name;
public String getName() {
return name;
}
}
Below, a unit test using your input shows that everything work:
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);
}
}
This tests shows that without using dedicated classes, your code will quickly be hard to read.
You will need those maven dependencies to run it:
<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>
回答2:
{
"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"
},
来源:https://stackoverflow.com/questions/5820028/how-to-use-some-indirection-when-unmarshalling-json-to-java-bean-using-jersey-us