Losing type element when serializing object inside ArrayList to XML - xml-serialization

I seem to be experiencing a problem when using Jackson to serialize to XML. My code is below:
TEST CONTAINER
package com.test;
import java.util.ArrayList;
import com.fasterxml.jackson.annotation.JsonProperty;
public class TestContainer {
private String testContainerID;
private String testContainerMessage;
private ArrayList<TestChild> testContainerChildren;
#JsonProperty("TestContainerID")
public String getTestContainerID() {
return testContainerID;
}
#JsonProperty("TestContainerID")
public void setTestContainerID(String testContainerID) {
this.testContainerID = testContainerID;
}
#JsonProperty("TestContainerMessage")
public String getTestContainerMessage() {
return testContainerMessage;
}
#JsonProperty("TestContainerMessage")
public void setTestContainerMessage(String testContainerMessage) {
this.testContainerMessage = testContainerMessage;
}
#JsonProperty("TestContainerChildren")
public ArrayList<TestChild> getTestContainerChildren() {
return testContainerChildren;
}
#JsonProperty("TestContainerChildren")
public void setTestContainerChildren(ArrayList<TestChild> testContainerChildren) {
this.testContainerChildren = testContainerChildren;
}
}
TESTCHILD
package com.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName(value="TestChild")
public class TestChild {
private String testChildID;
private String testChildMessage;
#JsonProperty("TestChildID")
public String getTestChildID() {
return testChildID;
}
#JsonProperty("TestChildID")
public void setTestChildID(String testChildID) {
this.testChildID = testChildID;
}
#JsonProperty("TestChildMessage")
public String getTestChildMessage() {
return testChildMessage;
}
#JsonProperty("TestChildMessage")
public void setTestChildMessage(String testChildMessage) {
this.testChildMessage = testChildMessage;
}
}
USE
Serialization:
XmlMapper xm = new XmlMapper();
TestContainer tc = xm.readValue(sb.toString(), TestContainer.class);
Deserialization:
System.out.println(xm.writeValueAsString(tc));
tc = xm.readValue(sb.toString(), TestContainer.class);
What I'm doing is loading an XML file from a folder on the classpath and putting the contents of the file into a StringBuffer. The problem is the generated XML for the collection of objects. When writing the XML, I want something like:
<TestContainerChildren><TestChild><...(Element Details)...></TestChild></TestContainerChildren>
but I'm getting:
<TestContainerChildren><TestContainerChildren><...(Element Details)...><TestContainerChildren></TestContainerChildren>
I'm not sure what I'm missing, here. I have no problem with the JSON part of the serialization/deserialization, only the XML. I've tried using both Jackson and JAXB annotations to turn off wrapping, I have tried using the following annotations:
#JsonRootName
#JsonProperty
#JacksonXmlElementWrapper
#JacksonElement
#XmlElementWrapper
#XmlElement
I'm pretty sure this is something stupid on my part, but any help would be most appreciated.

Ok, couple of notes. First, #JsonRootName only affects name used for the root of XML document, as name implies. So it is not used for TestChild. Second, it sounds like you want to use so-called "unwrapped" output for Lists, omitting element for property that contains List elements. This is doable with:
#JacksonXmlElementWrapper(useWrapping=false)
#JsonProperty("TestContainerChildren")
public ArrayList<TestChild> getTestContainerChildren() { ... }
since default setting is to use wrapper (this is different from JAXB, where unwrapped is the default). Or, if you want to change this globally to assume unwrapped as default, you can change the defaults via XmlModule:
JacksonXmlModule module = new JacksonXmlModule();
// to default to using "unwrapped" Lists:
module.setDefaultUseWrapper(false);
XmlMapper xmlMapper = new XmlMapper(module);
Hope this helps!

I got this working by using the following annotations above the variable declaration:
#JacksonXmlElementWrapper(localName="[insert collection name]")
#JacksonXmlProperty(localName="[insert collection element name]")
This was a simple case of RTFM, as it's documented here.

Related

How to add structural links to Jersey/Moxy/JAXB XML without altering the model

I mean "structural links" in the HATEOAS/hypermedia API sense. The more general question is how to augment the generated XML with data that depends on both the entity being marshalled, and also on the environment (in this case, at least the absolute URL).
I'm using Jersey 2.9 with Moxy 2.5 as the JAXB provider.
From this model:
package testing;
import java.util.ArrayList;
import java.util.List;
public class Planet {
private int id = 1;
private String name = "test";
private double radius = 3.0;
private String href;
private List<Moon> moons = new ArrayList<Moon>(0);
public void addMoon(Moon moon) {
moons.add(moon);
}
}
...plus Moon class
I want to get something like this XML (and the equivalent JSON):
<planet href="http://mytestserver/rest/planets/test">
<name>test</name>
<radius>3.0</radius>
<moons>
<moon href="http://mytestserver/rest/moons/moon1">
<name>moon1</name>
</moon>
<moon href="http://mytestserver/rest/moons/moon2">
<name>moon2</name>
</moon>
</moons>
</planet>
The model has no "href" field, nor can one be added. Ideally I could use UriBuilder to grab these paths straight from the resource classes.
So far I've come up with several possiblities. Can I ask you to consider which (if any) has the most legs, and then how you would work around the shortcomings of that method?
1. Augment the model with AspectJ (or Javassist).
And then use the existing declarative linking mechanisms in Jersey, all of which rely on there being a field in the model to receive the generated links. This obviously won't work if you don't have AspectJ in your build process and/or balk at exotic techniques like byte code manipulation.
2. Post-process the generated XML and JSON
For example, in a MessageBodyWriter:
ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
JAXBContext context = resolver.getContext(type);
Marshaller m = context.createMarshaller();
<--- here, marshall to e.g. a DOM then transform that
<--- then manipulate the JSON structures
I have absolutely no idea how to do any of that, hence the lack of code. There may be other ways to hook into the XML generation process, but as far as I can see none of Jersey's or JAXB's event handlers or interceptors actually allow you to manipulate the generated XML/JSON.
3. Use a Moxy XMLTransformationMapping
For example:
XML binding:
<java-type name="Planet" xml-customizer="testing.HrefCustomizer">
Customizer:
public class HrefCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
XMLTransformationMapping xtm = new XMLTransformationMapping();
xtm.addFieldTransformer("#href", new HrefWriter());
descriptor.addMapping(xtm);
}
}
Transformer:
public class HrefWriter implements FieldTransformer {
#Override
public Object buildFieldValue(Object instance, String fieldName,
Session session) {
return "href"; // constant value just for proof-of-concept
}
#Override
public void initialize(AbstractTransformationMapping mapping) {
// TODO Auto-generated method stub
}
}
I have two problems with this approach:
It was so hard to find any documentation on it that I wonder if it is in fact unsupported usage.
I can't see how the transformer is going to get a UriBuilder to work with. At minimum it would need the root URL of the rest service.
4. Slightly different Moxy xml-transform approach
If we decide we can't provide the transformer with any meaningful context at instantiation time, the customizer is adding no value and we can simplify the above to just this:
<java-type name="Planet">
<xml-root-element/>
<java-attributes>
<xml-transformation java-attribute="name">
<xml-write-transformer transformer-class="testing.HrefWriter" xml-path="#href"/>
</xml-transformation>
<xml-element java-attribute="name"/>
With the slight oddity that we are hanging the transformer off another field ("name", in this example).
5. ?????
Or, I'm completely barking up the wrong tree. Help!!
AspectJ approach
Synopsis
Use AspectJ to add a field to the model classes (called "href" in this example)
Add the Jersey #InjectLink annotation to that field
Jersey will then populate the field with the right URL as defined by the resource class
Specify the marshaling of the href field using an external mapping file.
You could also specify the marshaling of href by adding JAXB annotations to it via the same AspectJ intertype declaration mechanism.
Example code
These are the most informative bits. See http://lagod.id.au/blog/?p=494 for the full example.
The aspect
package testing;
import org.glassfish.jersey.linking.InjectLink;
import org.glassfish.jersey.linking.Binding;
public aspect HrefInjector {
private String Planet.href;
declare #field : * Planet.href : #InjectLink(
resource=Services.class,
style=InjectLink.Style.ABSOLUTE
) ;
private String Moon.href;
declare #field : * Moon.href : #InjectLink(
resource=Services.class,
method="moon",
bindings={#Binding(
name="moonid", value="${instance.name}"
)},
style=InjectLink.Style.ABSOLUTE
) ;
}
Model classes
POJOs with no REST-specific cruft. See Jersey + Moxy + JAXB - how to marshal XML without annotations.
package testing;
import java.util.ArrayList;
import java.util.List;
public class Planet {
private int id = 1;
private String name = "test";
private double radius = 3.0;
private List<Moon> moons = new ArrayList<Moon>(0);
public void addMoon(Moon moon) {
moons.add(moon);
}
}
package testing;
public class Moon {
private String name;
// No-arg constructor is a requirement of JAXB
public Moon() {
}
public Moon(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Resource class
This is a standard JAX-RS resource class. For demo purposes, we're just returning freshly instantiated model instances.
package testing;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/services")
#Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public class Services {
private Planet initPlanet() {
Planet p = new Planet();
p.addMoon(new Moon("moon1"));
p.addMoon(new Moon("moon2"));
return p;
}
#GET
public Planet planet () {
return initPlanet();
}
#GET #Path("/moons/{moonid}")
public Moon moon (#PathParam("moonid") String name) {
return new Moon(name);
}
}
Moxy mapping file
Note that you can choose for any given type whether or not you want to actually marshal the href field. In fact, by using multiple mapping files, you can include the href field in some representations and not in others.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="testing"
xml-mapping-metadata-complete="true"
xml-accessor-type="NONE">
<java-types>
<java-type name="Planet">
<xml-root-element/>
<java-attributes>
<xml-attribute java-attribute="href"/>
<xml-element java-attribute="name"/>
<xml-element java-attribute="radius"/>
<xml-element java-attribute="moons" name="moon">
<xml-element-wrapper name="moons"/>
</xml-element>
</java-attributes>
</java-type>
<java-type name="Moon">
<xml-root-element/>
<java-attributes>
<xml-attribute java-attribute="href"/>
<xml-element java-attribute="name"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Sample output
Ta-dah! Structural links derived automatically from the JAX-RS resource class without altering model source code. Because we're using Moxy, we also get JSON for free.
<planet href="http://localhost:8080/reststructlinks/rest/services">
<name>test</name>
<radius>3.0</radius>
<moons>
<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon1">
<name>moon1</name>
</moon>
<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon2">
<name>moon2</name>
</moon>
</moons>
</planet>

Jackson and REST Android Annotations: Deserialize JSON Array of Objects

I have a REST service which returns data that looks like this:
[
{ bookmark object json here },
{ bookmark object json here },
{ bookmark object json here },
...
]
My REST client class looks like this:
#Rest(rootUrl = Constants.ApiConfig.API_ROOT, converters = {MappingJackson2HttpMessageConverter.class})
public interface RestApiClient {
#Get("/bookmark/read?id={identifier}")
public BookmarkList getBookmarks(String identifier);
}
BookmarkList looks like this:
public class BookmarkList {
List<Bookmark> bookmarks;
#JsonValue
public List<Bookmark> getBookmarks() {
return bookmarks;
}
#JsonCreator
public void BookmarkList(#NotNull List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
}
However, when I utilize this setup, I get the following error:
Could not read JSON: Can not deserialize instance of com.whatever.entity.BookmarkList out of START_ARRAY token
What I want is something like the EventList example at https://github.com/excilys/androidannotations/wiki/Rest-API#get, but that doesn't seem to work for me out of the box.
Is there a way to get this working?
Ho... We have to update this part of documentation. The wrapper solution works but doesn't fit APIs.
If you're looking at the generated code for #Get("url") MyReturnedType testService(), you should see something like this :
return restTemplate.exchange(rootUrl.concat("url"), //
HttpMethod.GET, //
null, //
MyReturnedType.class, //
urlVariables)//
.getBody();
The returned class is injected as a parameter of exchange call. In case of generics collection (like List<MyReturnedType>), we can't inject List.class because of type checking in the return of exchange method.
However, you should be able to use this little trick in your #Rest annotated method :
public class BookmarkList extends List<Bookmark> {
}
I think I misunderstood the example at https://github.com/excilys/androidannotations/wiki/Rest-API#get. I think the array still must be wrapped inside a JSON object in that example (It'd be nice if they included example JSON data).
The data the service I'm connecting to does not return an object wrapping the array like that, so, I altered the REST client to look like this:
#Rest(rootUrl = Constants.ApiConfig.API_ROOT, converters = {MappingJackson2HttpMessageConverter.class})
public interface RestApiClient {
#Get("/bookmark/read?id={identifier}")
public ArrayNode getBookmarks(String identifier);
}
And I wrote a method in another class to iterate the ArrayNode and build the bookmarks:
public List<Bookmark> getBookmarks(Content content) {
ArrayList<Bookmark> bookmarks = new ArrayList<Bookmark>();
ArrayNode bookmarksData = apiClient.getBookmarks(content.getAcid());
for(JsonNode bookmarkData : bookmarksData) {
Bookmark bookmark = objectMapper.convertValue(bookmarkData, Bookmark.class);
bookmarks.add(bookmark);
}
return bookmarks;
}
So it's not as convenient (I had to write more code myself), but I got it working.

How do I create a macro for property extensions

I'd like to write my own macro for creating property like objects in Haxe.
This question is not so much about properties but more about writing macros.
(probably NME has already a macro for that).
having this class in haxe
class Foo {
#:property var bar:String;
}
I like this to be expanded into
class Foo {
private var bar:String;
public function setBar(_val:String):void {
this.bar = _val;
}
public function getBar():String {
return this.bar;
}
}
I read the corresponding docs but honestly I find them very confusing.
thanks
You might want to take a look at how tinkerbell resolves the same issue: https://github.com/back2dos/tinkerbell/wiki/tink_lang#wiki-accessors
This Type Builder example (pasted below for reference, but there's better description at the link) found in the Haxe Manual is a nice, simple example of adding a function to a Class.
Adding a property would be much the same. I added a trace(field) loop to help get a feel for how they're defined:
Main.hx
#:build(TypeBuildingMacro.build("myFunc"))
class Main {
static public function main() {
trace(Main.myFunc); // my default
}
}
TypeBuildingMacro.hx
import haxe.macro.Context;
import haxe.macro.Expr;
class TypeBuildingMacro {
macro static public function build(fieldName:String):Array<Field> {
var fields = Context.getBuildFields();
for (field in fields) { trace(field); }
var newField = {
name: fieldName,
doc: null,
meta: [],
access: [AStatic, APublic],
kind: FVar(macro : String, macro "my default"),
pos: Context.currentPos()
};
fields.push(newField);
return fields;
}
}
Note that Main.hx must invoke the macro with the #:build metadata, so the compiler knows to run the macro (which adds the function) before processing the Main class itself.

MEF: how to import from an exported object?

I have created a MEF plugin control that I import into my app. Now, I want the plugin to be able to import parts from the app. I can't figure how setup the catalog in the plugin, so that it can find the exports from the app. Can somebody tell me how this is done? Below is my code which doesn't work when I try to create an AssemblyCatalog with the current executing assembly.
[Export(typeof(IPluginControl))]
public partial class MyPluginControl : UserControl, IPluginControl
[Import]
public string Message { get; set; }
public MyPluginControl()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
CompositionContainer container = new CompositionContainer(catalog);
try
{
container.ComposeParts(this);
}
catch (CompositionException ex)
{
Console.WriteLine(ex.ToString());
}
}
}
You don't need to do this.
Just make sure that the catalog you're using when you import this plugin includes the main application's assembly.
When MEF constructs your type in order to export it (to fulfill the IPluginControl import elsewhere), it'll already compose this part for you - and at that point, will import the "Message" string (though, you most likely should assign a name to that "message", or a custom type of some sort - otherwise, it'll just import a string, and you can only use a single "string" export anywhere in your application).
When MEF composes parts, it finds all types matching the specified type (in this case IPluginControl), instantiates a single object, fills any [Import] requirements for that object (which is why you don't need to compose this in your constructor), then assigns it to any objects importing the type.

Grails: JSONP callback without id and class in JSON file

I am working on a REST based interface where people get a json file. The client needs to access the file from another Domain. I use jsonp which works so far. My problem is the rendering in Grails. At the moment I use the 'as JSON' to marshalling the object:
render "${params.jsoncallback}(${user as JSON})"
The Json file getting to the client inclused all attributes, incluing the id and class, which I do not want to have in there. In case it is not jsonp, I do it this way, which works great:
render(contentType:'text/json'){
userName user.userName
userImage user.userImage
:
:
}
So how do I get the id and class attributes out of the json when rendering "user as JSON"? Any idea?
best regards,
Klaas
You can get rid of the class and id properties in the JSON result by creating a custom ObjectMarshaller.
// CustomDomainMarshaller.groovy in src/groovy:
import grails.converters.JSON;
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller;
import org.codehaus.groovy.grails.web.json.JSONWriter;
import org.springframework.beans.BeanUtils;
public class CustomDomainMarshaller implements ObjectMarshaller<JSON> {
static EXCLUDED = ['metaClass','class','id','version']
public boolean supports(Object object) {
return ConverterUtil.isDomainClass(object.getClass());
}
public void marshalObject(Object o, JSON json) throws ConverterException {
JSONWriter writer = json.getWriter();
try {
writer.object();
def properties = BeanUtils.getPropertyDescriptors(o.getClass());
for (property in properties) {
String name = property.getName();
if(!EXCLUDED.contains(name)) {
def readMethod = property.getReadMethod();
if (readMethod != null) {
def value = readMethod.invoke(o, (Object[]) null);
writer.key(name);
json.convertAnother(value);
}
}
}
writer.endObject();
} catch (Exception e) {
throw new ConverterException("Exception in CustomDomainMarshaller", e);
}
}
}
You'll need to register in you grails-app/conf/BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
grails.converters.JSON.registerObjectMarshaller(new CustomDomainMarshaller())
}
def destroy = {}
}
This should work in Grails >= 1.1
thanks for the 'quick' reply!
Man, it looks so easy in the end and took so long to figure out.
I got it working doing a map out of the values I needed and rendered them 'as json' like this:
def userProfile = user.get(randomUser)
def jsonData = [
username: userProfile.userName,
userimage: userProfile.userImage,
userstreet: userProfile.userStreet,
:
:
] as JSON
println jsonData
voila, there was the json I needed :)
It doesn't seem to me as if the JSON auto marshaller supports this.
You could use FlexJSON which allows you to exlude certain properties and wrap it into a custom Codec.
Also see here.