Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
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 Go to next message
Clovis Wichoski is currently offline Clovis WichoskiFriend
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 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise DoughanFriend
Messages: 163
Registered: July 2009
Senior Member

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 #1230082 is a reply to message #1229993] Fri, 10 January 2014 23:55 Go to previous messageGo to next message
Clovis Wichoski is currently offline Clovis WichoskiFriend
Messages: 7
Registered: March 2012
Junior Member
Hi Blaise,

I dont used Marshaller directly I used the default behavior as explained in your post "MOXy as Your JAX-RS JSON Provider - MOXyJsonProvider" without setting any extra option, maybe for your case worked as you used JAXB_FORMATTED_OUTPUT? or I must setup some extra options for this case?

best regards

Clóvis
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1230936 is a reply to message #1230082] Mon, 13 January 2014 12:15 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise DoughanFriend
Messages: 163
Registered: July 2009
Senior Member

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 #1231450 is a reply to message #1230936] Tue, 14 January 2014 18:01 Go to previous messageGo to next message
Clovis Wichoski is currently offline Clovis WichoskiFriend
Messages: 7
Registered: March 2012
Junior Member
Hi Blaise

I tried to use released version and the problems still.

I cant make a simple sample for a post or code snippet, I dont know a easy way to express that, then I make a maven project and a SOAP UI project for you simulate in your side.

I used NetBeans with Tomcat 7 to run the maven project, the project have a configuration for a memory derby database, for fast running.

To test the REST request I created a SOAP UI project, then to simulate the scenario you must run follow named requests:

1. createGrupo
2. createTela
3. getTelas

When you call on SOAP UI the getTelas request you will see the wrong JSON response.

Hope that this can help us to find the problem.

Best regards

Clóvis
Re: @XmlInverseReference generate wrong JSON for bidirectional relations [message #1249071 is a reply to message #1231450] Mon, 17 February 2014 23:51 Go to previous messageGo to next message
Christopher Parker is currently offline Christopher ParkerFriend
Messages: 2
Registered: January 2012
Junior Member

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
List<Child> children
and the Child entity contains
List<Parent> parents
.

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 Go to previous message
Christopher Parker is currently offline Christopher ParkerFriend
Messages: 2
Registered: January 2012
Junior Member

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

Previous Topic:EclipseLink memory leak?
Next Topic:Invoke JPA entity creation process from a script
Goto Forum:
  


Current Time: Fri Nov 28 02:01:57 GMT 2014

Powered by FUDForum. Page generated in 0.02625 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software