Home » Eclipse Projects » EclipseLink » @XmlInverseReference generate wrong JSON for bidirectional relations(@XmlInverseReference works with XML but generate wrong JSON for bidirectional oneToMany - ManyToone JPA)
@XmlInverseReference generate wrong JSON for bidirectional relations [message #1228546] |
Tue, 07 January 2014 13:24 |
Clovis Wichoski Messages: 7 Registered: March 2012 |
Junior Member |
|
|
Hi,
I'm using EclipseLink JPA and MOXy in a new project to develop a REST API, the versions for eclipselink and org.eclipse.persistence.moxy are 2.5.1-SNAPSHOT, The application is running on Tomcat 7 with Jersey, then I used jaxb.properties file with javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
for JSON I registered org.eclipse.persistence.jaxb.rs.MOXyJsonProvider as the JSON Provider for my REST resources.
for XML all works fine the problem is only with JSON.
I have two classes that have bidirectional references.
@Entity
@XmlRootElement
public class GrupoTela implements Serializable {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
private Integer ordem;
private String icone;
@XmlElement
@XmlInverseReference(mappedBy = "grupoTela")
@OneToMany(fetch = FetchType.EAGER, cascade = ALL, mappedBy = "grupoTela")
private ArrayList<Tela> telaList;
//accessors suppressed for easy reading...
}
@Entity
@XmlRootElement
public class Tela implements Serializable {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String titulo;
private String comando;
@XmlElement
@XmlInverseReference(mappedBy = "telaList")
@ManyToOne @JoinColumn(name = "idGrupoTela", nullable = false)
private GrupoTela grupoTela;
//accessors suppressed for easy reading...
}
if do a REST call for one GrupoTela I receive this correct JSON as reply:
{"telaList":[{"comando":"cad/grupoTela","id":1,"titulo":"Grupo de Tela"},{"comando":"cad/tela","id":2,"titulo":"Tela"}],"icone":"fa fa-wrench","id":2,"nome":"Sistema","ordem":1}
but if do a REST call for one Tela I receive this incorrect JSON as reply with Unexpected token }:
{"grupoTela":{"icone":["fa fa-wrench",2,"Sistema",1},"comando":"cad/tela","id":2,"titulo":"Tela"}
a close look at response of Tela we can see that on grupoTela reference the attribute icone was generated like an array of other attributes that was incorrect based on mapping.
The expected JSON is:
{"grupoTela":{"icone":"fa fa-wrench","id":2,"nome":"Sistema","ordem":1},"comando":"cad/tela","id":2,"titulo":"Tela"}
Then my doubt is this is a BUG or I made some mistake on mapping this?
|
|
|
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1229993 is a reply to message #1228546] |
Fri, 10 January 2014 18:58 |
|
Hi Clovis,
You code works for me when I used the released version of EclipseLink 2.5.1. Using your model, when I run the following code:
package el634990;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Tela.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/el634990/input.json");
Tela tela = unmarshaller.unmarshal(json, Tela.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(tela, System.out);
}
}
I get output matching the input.
{
"id" : 2,
"titulo" : "Tela",
"comando" : "cad/tela",
"grupoTela" : {
"id" : 2,
"nome" : "Sistema",
"ordem" : 1,
"icone" : "fa fa-wrench"
}
}
-Blaise
|
|
| |
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1230936 is a reply to message #1230082] |
Mon, 13 January 2014 12:15 |
|
Hi Clovis,
I am still trying to reproduce the issue you are seeing. When I run the following code:
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
...
MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
moxyJsonProvider.writeTo(tela, Tela.class, Tela.class, null, null, null, System.out);
I get the following as output:
{"id":2,"titulo":"Tela","comando":"cad/tela","grupoTela":{"id":2,"nome":"Sistema","ordem":1,"icone":"fa fa-wrench"}}
Is it possible for you to put together a short example that reproduces the error you are seeing? Also you mentioned you are using a snapshot. Do you still see the problem when you upgrade to the released version.
-Blaise
|
|
| |
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1249071 is a reply to message #1231450] |
Mon, 17 February 2014 23:51 |
|
This issue is happening for me, as well. I've noticed this happening in particular when two entities have both OneToMany and ManyToOne references to each other, and an entity being marshalled ultimately contains a reference to itself.
For example, suppose you have a Parent entity and a Child entity. The Parent entity contains and the Child entity contains .
With the following basic example, I receive the same error (for example, using JSON Lint)...
Parent parent = new Parent();
Child child = new Child();
parent.addChild(child);
child.addParent(parent);
moxyJsonProvider.writeTo(parentA, Parent.class, Parent.class, null, null, null, System.out);
I'll post a complete example--likely via GitHub--demonstrating the problem shortly.
|
|
|
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1292177 is a reply to message #1249071] |
Fri, 11 April 2014 15:36 |
|
I have an even simpler concrete example that doesn't require a double inverse reference. I'm not going to bother with a GitHub repo.
Account class, with a composite primary key, and a list of transactions for the account.
Account:
package com.example.model;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@Entity
@Table(name = "ACCOUNTS")
@XmlRootElement
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
@XmlElement
private AccountPK id;
// bi-directional many-to-one association to Transaction
@OneToMany(mappedBy = "account", fetch = FetchType.EAGER)
@XmlElement
private List<Transaction> transactions;
@Column(name = "DESC", nullable = false, length = 30)
@XmlElement
private String description;
public Account() {
}
}
AccountPK:
package com.example.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlAttribute;
@Embeddable
public class AccountPK implements Serializable {
// default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
@Column(name = "BRANCH_CODE", unique = true, nullable = false, length = 2)
@XmlAttribute
private String branchCode;
@Column(name = "NUM", unique = true, nullable = false, length = 8)
@XmlAttribute
private String number;
public AccountPK() {
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AccountPK)) {
return false;
}
AccountPK castOther = (AccountPK) other;
return this.branchCode.equals(castOther.branchCode)
&& this.number.equals(castOther.number);
}
public String getBranchCode() {
return this.branchCode;
}
public String getNumber() {
return this.number;
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.branchCode.hashCode();
hash = hash * prime + this.number.hashCode();
return hash;
}
public void setBranchCode(String branchCode) {
this.branchCode = branchCode;
}
public void setNumber(String number) {
this.number = number;
}
}
Transaction:
package com.example.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
@Entity
@Table(name = "TRANSACTIONS")
@XmlRootElement
public class Transaction implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "TXID", unique = true, nullable = false, precision = 10)
@XmlElement
private long id;
// bi-directional many-to-one association to Account
@ManyToOne
@JoinColumns({
@JoinColumn(name = "NUM", referencedColumnName = "NUM"),
@JoinColumn(name = "BRANCH_CODE", referencedColumnName = "BRANCH_CODE") })
@XmlInverseReference(mappedBy = "transactions")
@XmlElement
private Account account;
@Column(name = "DESC", nullable = false, length = 30)
@XmlElement
private String description;
public Transaction() {
}
public Account getAccount() {
return account;
}
}
Here's the code I'm using to marshal each:
Transaction transaction = entityManager.find(Transaction.class, 0);
MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
try {
System.out.println("Transaction:");
moxyJsonProvider.writeTo(transaction, Transaction.class, Transaction.class,
null, null, null, System.out);
System.out.println("");
System.out.println("Account:");
moxyJsonProvider.writeTo(transaction.getAccount(),
Account.class, Account.class, null, null,
null, System.out);
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
This is what I get when I marshal an Account (no problems):
{
"id": {
"branchCode": "XX",
"number": "12345678"
},
"transactions": [
{
"id": 0,
"description": "Transaction 1 Desc"
},
{
"id": 1,
"description": "Transaction 2 Desc"
},
{
"id": 2,
"description": "Transaction 3 Desc"
},
{
"id": 3,
"description": "Transaction 4 Desc"
},
{
"id": 4,
"description": "Transaction 5 Desc"
},
{
"id": 5,
"description": "Transaction 6 Desc"
},
{
"id": 6,
"description": "Transaction 7 Desc"
}
],
"description": "Account XX-12345678 Desc"
}
This is what I get when I marshal a Transaction (malformed JSON):
{
"id": 0,
"account": {
"id": {
"branchCode": "XX",
"number": "12345678"
},
"description": [
"Account XX-12345678 Desc"
},
"description": "Transaction 1 Desc"
}
Note the opening array bracket after "description" that shouldn't be there.
This is what JSONLint thinks the problem is:
Parse error on line 9:
...45678 Desc" }, "descript
----------------------^
Expecting ',', ']'
This is what the JSON should look like, instead... I'm assuming there should be other transactions listed here for the account (transactions with IDs 1 - 6, and obviously excluding transaction with ID 0), but I've never seen a working example, so I don't know for sure what I should expect.
{
"id": 0,
"account": {
"id": {
"branchCode": "XX",
"number": "12345678"
},
"description": "Account XX-12345678 Desc"
},
"description": "Transaction 1 Desc"
}
I'm not sure how to proceed with MOXy as a result of this issue, as it's a show-stopper. Hopefully I'm just missing something, but this does seem like a bug to me.
Note: I'm using EclipseLink MOXy 2.5.1 from Maven Central and JDK 7. From my POM:
<!-- snip -->
<properties>
<eclipselink.version>2.5.1</eclipselink.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>${eclipselink.version}</version>
</dependency>
</dependencies>
<!-- snip -->
Thanks!
[Updated on: Fri, 11 April 2014 15:41] Report message to a moderator
|
|
|
Goto Forum:
Current Time: Sun Sep 22 22:09:38 GMT 2024
Powered by FUDForum. Page generated in 0.03657 seconds
|