In a previous post I described how the Jettison library could be used with any JAXB implementation (Metro, MOXy, JaxMe, etc) to produce/consume JSON. Now I'll demonstrate the native object-to-JSON binding MOXy JAXB introduced in EclipseLink 2.4. With MOXy as your JAXB provider you can produce/consume JSON using the standard JAXB APIs (available in Java SE 6) without adding any compile time dependencies.
You can try this out now using a download available from:
You can try this out now using a download available from:
Twitter Query
In this example our JSON corresponds to the results of a query to get recent tweets about JAXB. We will use the Twitter Search API for this.
URL
Below is the URL corresponding to the search. We are looking for posts containing the word "jaxb", and want the result returned as JSON:
http://search.twitter.com/search.json?q=jaxb
Result (Formatted)
Below is what the result of the query looked like at the time I was writing this post (I'm surprised that none of them were from me):
{ "completed_in":0.153, "max_id":101941099769245696, "max_id_str":"101941099769245696", "next_page":"?page=2&max_id=101941099769245696&q=jaxb", "page":1, "query":"jaxb", "refresh_url":"?since_id=101941099769245696&q=jaxb", "results":[ { "created_at":"Fri, 12 Aug 2011 09:00:26 +0000", "from_user":"44aki110", "from_user_id":92669210, "from_user_id_str":"92669210", "geo":null, "id":101941099769245696, "id_str":"101941099769245696", "iso_language_code":"ja", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a2.twimg.com/profile_images/1301749249/44aki110_normal.jpg", "source":"<a href="http://www.soicha.com" rel="nofollow">SOICHA</a>", "text":"CCD\u30E1\u30C3\u30BB\u30FC\u30B8\u3092JAXB\u3067\u30A2\u30F3\u30DE\u30FC\u30B7\u30E3\u30EB\u3057\u3066\u3001Java\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u4F5C\u308D\u3046\u3068\u3057\u3066\u3044\u308B\u3093\u3060\u3051\u3069\u3001CDA\u306E\u30EB\u30FC\u30EB\u3068JAXB\u306E\u4ED5\u69D8\u304C\u3046\u307E\u304F\u5408\u308F\u306A\u304F\u3066\u3001\u306A\u304B\u306A\u304B\u4F5C\u6210\u3067\u304D\u306A\u3044\u2026\u56F0\u3063\u305F\u306A\u3041\u3002", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Fri, 12 Aug 2011 01:14:57 +0000", "from_user":"stackfeed", "from_user_id":212341639, "from_user_id_str":"212341639", "geo":null, "id":101823955765170176, "id_str":"101823955765170176", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a2.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", "source":"<a href="http://twitterfeed.com" rel="nofollow">twitterfeed</a>", "text":"How to customise the XML output of a Jersey JAXB serialisation: I have some @javax.xml.bind.annotation.Xml... an... http://bit.ly/ogK4Xg", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Fri, 12 Aug 2011 00:55:22 +0000", "from_user":"Keisuke69", "from_user_id":21769095, "from_user_id_str":"21769095", "geo":null, "id":101819027122434048, "id_str":"101819027122434048", "iso_language_code":"ja", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a3.twimg.com/profile_images/1096911570/xx_normal.jpg", "source":"<a href="http://twipple.jp/" rel="nofollow">\u3064\u3044\u3063\u3077\u308B/twipple</a>", "text":"Spring\u3067Jaxb2RootElementHttpMessageConverter\u4F7F\u3063\u3066XML\u3067\u30EC\u30B9\u30DD\u30F3\u30B9\u3059\u308B\u5834\u5408\u3001List\u3092\u8FD4\u3057\u305F\u3089\u30A8\u30E9\u30FC\u306B\u306A\u308B\u3093\u3060\u304C\u3053\u308C\u3063\u3066\u4ED5\u69D8\u306A\u306E\u304B\u306A\u3002\u540C\u4E00\u30E1\u30BD\u30C3\u30C9\u3067JSON\u306E\u5834\u5408\u306F\u554F\u984C\u306A\u3044\u306E\u306B\u3002@ResponseBody List<Hoge>\u306F\u30C0\u30E1?", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Fri, 12 Aug 2011 00:55:16 +0000", "from_user":"johnniefeed", "from_user_id":197698180, "from_user_id_str":"197698180", "geo":null, "id":101819001520398338, "id_str":"101819001520398338", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png", "source":"<a href="http://twitterfeed.com" rel="nofollow">twitterfeed</a>", "text":"How do you pronounce JAXB?: You know, must suchlike acronyms have alternative pronunciations. URL is pronounced ... http://bit.ly/qsJfHJ", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 22:31:31 +0000", "from_user":"JavaAtSO", "from_user_id":149995217, "from_user_id_str":"149995217", "geo":null, "id":101782826130751488, "id_str":"101782826130751488", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a2.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", "source":"<a href="http://twitterfeed.com" rel="nofollow">twitterfeed</a>", "text":"Given the following XSD definition, how do I separate the sub-elements into individual properties using JAXB? http://bit.ly/rtrnrt", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 22:20:09 +0000", "from_user":"elithp", "from_user_id":206373701, "from_user_id_str":"206373701", "geo":null, "id":101779964843663360, "id_str":"101779964843663360", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a0.twimg.com/sticky/default_profile_images/default_profile_5_normal.png", "source":"<a href="http://twitterfeed.com" rel="nofollow">twitterfeed</a>", "text":"Given the following XSD definition, how do I separate the sub-elements into individual properties using JAXB?: I... http://bit.ly/p1X2zF", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 20:02:10 +0000", "from_user":"botimer", "from_user_id":1682015, "from_user_id_str":"1682015", "geo":null, "id":101745240007909377, "id_str":"101745240007909377", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a3.twimg.com/profile_images/816270697/domo-coffee_normal.jpg", "source":"<a href="http://twitter.com/">web</a>", "text":"@sgithens but first, swing and jaxb.", "to_user":"sgithens", "to_user_id":29289749, "to_user_id_str":"29289749" }, { "created_at":"Thu, 11 Aug 2011 19:51:04 +0000", "from_user":"EarthwormJames", "from_user_id":35728922, "from_user_id_str":"35728922", "geo":null, "id":101742448258191361, "id_str":"101742448258191361", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a3.twimg.com/profile_images/1091025186/marvin_the_martian_jackets_normal.jpg", "source":"<a href="http://www.linkedin.com/" rel="nofollow">LinkedIn</a>", "text":"Currently working through an interesting issue with Jackson JSON and JAXB functionality on the Android platform and generally having lots o\u2026", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 13:06:55 +0000", "from_user":"hardillb", "from_user_id":68436, "from_user_id_str":"68436", "geo":null, "id":101640740484030464, "id_str":"101640740484030464", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a1.twimg.com/profile_images/67352717/BenCrop1_normal.jpg", "source":"<a href="http://twitter.com/">web</a>", "text":"I have defeated JAXB (well for now)", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 12:44:57 +0000", "from_user":"cumbers", "from_user_id":75992, "from_user_id_str":"75992", "geo":null, "id":101635211875463168, "id_str":"101635211875463168", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a1.twimg.com/profile_images/439391403/RichTwitPic_normal.jpg", "source":"<a href="http://twitter.com/">web</a>", "text":"@hardillb What;s the error? I remember JAXB being an absolute pig sometimes to setup!", "to_user":"hardillb", "to_user_id":68436, "to_user_id_str":"68436" }, { "created_at":"Thu, 11 Aug 2011 12:42:48 +0000", "from_user":"hardillb", "from_user_id":68436, "from_user_id_str":"68436", "geo":null, "id":101634671124824066, "id_str":"101634671124824066", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a1.twimg.com/profile_images/67352717/BenCrop1_normal.jpg", "source":"<a href="http://twitter.com/">web</a>", "text":"Trying to get eclipse JAXB code generator to work but even with @cumbers http://t.co/RJwlK5k not getting any where", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Thu, 11 Aug 2011 12:07:12 +0000", "from_user":"njames4", "from_user_id":35118299, "from_user_id_str":"35118299", "geo":null, "id":101625710946426880, "id_str":"101625710946426880", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a2.twimg.com/profile_images/1472255453/ego_normal.jpg", "source":"<a href="http://twitter.com/">web</a>", "text":"An OpenSearch XML schema that is JAXB friendly! http://t.co/E4wCe6R :-D", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Wed, 10 Aug 2011 20:15:20 +0000", "from_user":"BestScreener", "from_user_id":380723929, "from_user_id_str":"380723929", "geo":null, "id":101386165357264896, "id_str":"101386165357264896", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a0.twimg.com/profile_images/1481898889/kim_normal.jpg", "source":"<a href="http://www.busystock.com" rel="nofollow">busystock</a>", "text":"$BABS $JAXB $SYPR $ESLOF.PK $NEBS $KNCAY.PK $PRGAF.PK $DFIHY.PK Earnings Research data for past 10+ years. http://j.mp/fZloO2", "to_user_id":null, "to_user_id_str":null }, { "created_at":"Wed, 10 Aug 2011 18:38:51 +0000", "from_user":"olivergierke", "from_user_id":3398171, "from_user_id_str":"3398171", "geo":null, "id":101361887052181506, "id_str":"101361887052181506", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a2.twimg.com/profile_images/69836753/P1000914_normal.JPG", "source":"<a href="http://itunes.apple.com/us/app/twitter/id409789998?mt=12" rel="nofollow">Twitter for Mac</a>", "text":"@achingbrain It's usually a sign of a class being used for multiple purposes\u2026 In your case persistence might make sense but why JAXB?", "to_user":"achingbrain", "to_user_id":61662094, "to_user_id_str":"61662094" }, { "created_at":"Wed, 10 Aug 2011 17:46:50 +0000", "from_user":"achingbrain", "from_user_id":61662094, "from_user_id_str":"61662094", "geo":null, "id":101348794146369537, "id_str":"101348794146369537", "iso_language_code":"en", "metadata":{ "result_type":"recent" }, "profile_image_url":"http://a1.twimg.com/profile_images/1213603001/02d_normal.png", "source":"<a href="http://twitter.com/#!/download/iphone" rel="nofollow">Twitter for iPhone</a>", "text":"Is it ok to have seven or eight annotations for every field? JPA/Spring Data, JAXB, Hibernate Search, etc...", "to_user_id":null, "to_user_id_str":null } ], "results_per_page":15, "since_id":0, "since_id_str":"0" }
Demo
The standard JAXB APIs are used the process the JSON message. The only difference between this demo code, and the code I post for XML examples is the setting of two properties: "eclipselink.media.type" to specify "application/json", and "eclipselink.json.include-root" to indicate there is no root node. These properties are set on the Marshaller (lines 13 and 14) and Unmarshaller (lines 26 and 27). Since the JSON message does not contain a root node that can be used to determine the corresponding object type, we will leverage one of the Unmarshaller methods that lets us specify it (line 16).
package blog.json.twitter; import java.util.Date; import javax.xml.bind.*; import javax.xml.transform.stream.StreamSource; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(SearchResults.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setProperty("eclipselink.media-type", "application/json"); unmarshaller.setProperty("eclipselink.json.include-root", false); StreamSource source = new StreamSource("http://search.twitter.com/search.json?q=jaxb"); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class); Result result = new Result(); result.setCreatedAt(new Date()); result.setFromUser("bdoughan"); result.setText("You can now use EclipseLink JAXB (MOXy) with JSON :)"); jaxbElement.getValue().getResults().add(result); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty("eclipselink.media-type", "application/json"); marshaller.setProperty("eclipselink.json.include-root", false); marshaller.marshal(jaxbElement, System.out); } }
Output
The following is the output from this example. Since we only mapped to a portion of the input message, our output message is smaller than the input message:
{ "completed_in" : 0.153, "query" : "jaxb", "results" : [ { "created_at" : "Fri, 12 Aug 2011 05:00:26 -0400", "from_user" : "44aki110", "text" : "CCDメッセージをJAXBã?§ã‚¢ãƒ³ãƒžãƒ¼ã‚·ãƒ£ãƒ«ã?—ã?¦ã€?Javaオブジェクトを作ã‚?ã?†ã?¨ã?—ã?¦ã?„ã‚‹ã‚“ã? ã?‘ã?©ã€?CDAã?®ãƒ«ãƒ¼ãƒ«ã?¨JAXBã?®ä»•æ§˜ã?Œã?†ã?¾ã??å?ˆã‚?ã?ªã??ã?¦ã€?ã?ªã?‹ã?ªã?‹ä½œæˆ?ã?§ã??ã?ªã?„…困ã?£ã?Ÿã?ªã??。" }, { "created_at" : "Thu, 11 Aug 2011 21:14:57 -0400", "from_user" : "stackfeed", "text" : "How to customise the XML output of a Jersey JAXB serialisation: I have some @javax.xml.bind.annotation.Xml... an... http://bit.ly/ogK4Xg" }, { "created_at" : "Thu, 11 Aug 2011 20:55:22 -0400", "from_user" : "Keisuke69", "text" : "Springã?§Jaxb2RootElementHttpMessageConverter使ã?£ã?¦XMLã?§ãƒ¬ã‚¹ãƒ?ンスã?™ã‚‹å ´å?ˆã€?Listã‚’è¿”ã?—ã?Ÿã‚‰ã‚¨ãƒ©ãƒ¼ã?«ã?ªã‚‹ã‚“ã? ã?Œã?“ã‚Œã?£ã?¦ä»•æ§˜ã?ªã?®ã?‹ã?ªã€‚å?Œä¸€ãƒ¡ã‚½ãƒƒãƒ‰ã?§JSONã?®å ´å?ˆã?¯å•?é¡Œã?ªã?„ã?®ã?«ã€‚@ResponseBody List<Hoge>ã?¯ãƒ€ãƒ¡?" }, { "created_at" : "Thu, 11 Aug 2011 20:55:16 -0400", "from_user" : "johnniefeed", "text" : "How do you pronounce JAXB?: You know, must suchlike acronyms have alternative pronunciations. URL is pronounced ... http://bit.ly/qsJfHJ" }, { "created_at" : "Thu, 11 Aug 2011 18:31:31 -0400", "from_user" : "JavaAtSO", "text" : "Given the following XSD definition, how do I separate the sub-elements into individual properties using JAXB? http://bit.ly/rtrnrt" }, { "created_at" : "Thu, 11 Aug 2011 18:20:09 -0400", "from_user" : "elithp", "text" : "Given the following XSD definition, how do I separate the sub-elements into individual properties using JAXB?: I... http://bit.ly/p1X2zF" }, { "created_at" : "Thu, 11 Aug 2011 16:02:10 -0400", "from_user" : "botimer", "text" : "@sgithens but first, swing and jaxb." }, { "created_at" : "Thu, 11 Aug 2011 15:51:04 -0400", "from_user" : "EarthwormJames", "text" : "Currently working through an interesting issue with Jackson JSON and JAXB functionality on the Android platform and generally having lots o…" }, { "created_at" : "Thu, 11 Aug 2011 09:06:55 -0400", "from_user" : "hardillb", "text" : "I have defeated JAXB (well for now)" }, { "created_at" : "Thu, 11 Aug 2011 08:44:57 -0400", "from_user" : "cumbers", "text" : "@hardillb What;s the error? I remember JAXB being an absolute pig sometimes to setup!" }, { "created_at" : "Thu, 11 Aug 2011 08:42:48 -0400", "from_user" : "hardillb", "text" : "Trying to get eclipse JAXB code generator to work but even with @cumbers http://t.co/RJwlK5k not getting any where" }, { "created_at" : "Thu, 11 Aug 2011 08:07:12 -0400", "from_user" : "njames4", "text" : "An OpenSearch XML schema that is JAXB friendly! http://t.co/E4wCe6R :-D" }, { "created_at" : "Wed, 10 Aug 2011 16:15:20 -0400", "from_user" : "BestScreener", "text" : "$BABS $JAXB $SYPR $ESLOF.PK $NEBS $KNCAY.PK $PRGAF.PK $DFIHY.PK Earnings Research data for past 10+ years. http://j.mp/fZloO2" }, { "created_at" : "Wed, 10 Aug 2011 14:38:51 -0400", "from_user" : "olivergierke", "text" : "@achingbrain It's usually a sign of a class being used for multiple purposes… In your case persistence might make sense but why JAXB?" }, { "created_at" : "Wed, 10 Aug 2011 13:46:50 -0400", "from_user" : "achingbrain", "text" : "Is it ok to have seven or eight annotations for every field? JPA/Spring Data, JAXB, Hibernate Search, etc..." }, { "created_at" : "Wed, 28 Mar 2012 15:56:55 -0400", "from_user" : "bdoughan", "text" : "You can now use EclipseLink JAXB (MOXy) with JSON :)" } ] }
Specify MOXy as the JAXB Provider (jaxb.properties)
To configure MOXy as your JAXB provider simply add a file named jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactoryFor more information see: Specifying EclipseLink MOXy as Your JAXB Provider.
Java Model
MOXy leverages the same annotations used to map objects to/from XML for its JSON binding. In upcoming posts I'll expand on how the XML binding metadata is interpreted for JSON.
SearchResults
package blog.json.twitter; import java.util.List; import javax.xml.bind.annotation.XmlElement; public class SearchResults { private String query; private float completedIn; List<Result> results; public String getQuery() { return query; } public void setQuery(String query) { this.query = query; } @XmlElement(name="completed_in") public float getCompletedIn() { return completedIn; } public void setCompletedIn(float completedIn) { this.completedIn = completedIn; } public List<Result> getResults() { return results; } public void setResults(List<Result> results) { this.results = results; } }
Result
package blog.json.twitter; import java.util.Date; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; public class Result { private Date createdAt; private String fromUser; private String text; @XmlElement(name="created_at") @XmlJavaTypeAdapter(DateAdapter.class) public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } @XmlElement(name="from_user") public String getFromUser() { return fromUser; } public void setFromUser(String fromUser) { this.fromUser = fromUser; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
DateAdapter
I have written a lot about how useful JAXB's XmlAdapter is in XML binding. XmlAdapter is equally powerful when used with JSON binding:
package blog.json.twitter; import java.text.SimpleDateFormat; import java.util.Date; import javax.xml.bind.annotation.adapters.XmlAdapter; public class DateAdapter extends XmlAdapter<String, Date> { private SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); @Override public String marshal(Date date) throws Exception { return dateFormat.format(date); } @Override public Date unmarshal(String string) throws Exception { return dateFormat.parse(string); } }
Further Reading
If you enjoyed this post, then you may also be interested in:
@Legal!
ReplyDeleteAdopted your sample for use with StAXON (JSON via StAX):
ReplyDeletehttps://github.com/beckchr/staxon/wiki/Using-JAXB
Hi Blaise
ReplyDeleteRecently I was doing some experiments with EclipseLink 2.4 JSON binding feature. I was playing with marshalling/unmarshalling json and xml data to/from java objects (with xml elements in different namespaces).
It turns out that when I specify NAMESPACE_PREFIX_MAPPER with non empty namespace prefixes the json marshal/unmarshall goes well. But when I want to put a blank namespase prefix (corresponding to the default namespace in XML) the marshal goes well (it generates json objects without namespace prefixes) but the unmarshall gives me an error saying that the element could not be found. Is this a normal behavior or is a bug in json unmarshalling procedure?
Best regards
George
Hi George,
DeleteWe are currently looking into this. Thank you for entering a bug, that is our preferred way of receiving issues. You can use the link below to track our progress on this issue. We hope to have it resolved soon.
- http://bugs.eclipse.org/391530
-Blaise
hi I am newbie to eclipeslinks, While running the demo program i am facaing below exception
ReplyDeleteException in thread "main" javax.xml.bind.PropertyException: name: eclipselink.media-type value: application/json
at javax.xml.bind.helpers.AbstractMarshallerImpl.setProperty(AbstractMarshallerImpl.java:338)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.setProperty(MarshallerImpl.java:532)
at com.oracle.example.Demo.main(Demo.java:38)
Pls advice..
Since the stack trace contains classes from the com.sun.xml.internal.bind.v2 package MOXy is not being picked up as your JAXB (JSR-222) provider. You can specify MOXy as your JAXB provider using a jaxb.properties file, see:
Delete- Specifying EclipseLink MOXy as Your JAXB Provider
Here is a link to an example (hosted on GitHub) that you can try out:
- http://github.com/bdoughan/blog20110819