Problem:
We are facing strange problems when marshalling JSONs objects including the following content {"#type":"xs:string"}. Marshalling of this object results in a NullPointerException. See the stack trace below:
java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.SAX2DOM.startElement(SAX2DOM.java:204)
at com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.closeStartTag(ToXMLSAXHandler.java:208)
at com.sun.org.apache.xml.internal.serializer.ToXMLSAXHandler.characters(ToXMLSAXHandler.java:528)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerHandlerImpl.characters(TransformerHandlerImpl.java:172)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.DomLoader.text(DomLoader.java:128)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.text(InterningXmlVisitor.java:78)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.processText(StAXStreamConnector.java:324)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleEndElement(StAXStreamConnector.java:202)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:171)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:355)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:334)
at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalJAXBElementFromJSON(BaseJSONUnmarshaller.java:108)
at com.sun.jersey.json.impl.BaseJSONUnmarshaller.unmarshalFromJSON(BaseJSONUnmarshaller.java:97)
at JerseyNPETest.testNPEUnmarshal(JerseyNPETest.java:20)
The problem occurs while getting the response from the external service and casting it implicity by glassfish (Simple REST call).
We investigated the problem and found that it is actually related to the JSON unmarshaller.
Testcase:
Marshalling -
To verify our finding, we created a class which contains a member of type Object named propertyA. Then we set the value of propertyA to "some value" and marshalled it using the default marshaller which results in the JSON string "{"#type":"xs:string","$":"some value"}".
Unmarshalling - Afterwards we used the default unmarsahller. The attempt to unmarshall this JSON string resulted in the mentioned exception.
See the test case below:
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.json.impl.BaseJSONUnmarshaller;
import org.junit.Test;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringReader;
public class JerseyNPETest {
private static final String ERROR = "{\"additionalObject\":{\"#type\":\"xs:string\",\"$\":\"some value\"}}";
#Test
public void testNPEUnmarshal() throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(AnObject.class);
final JSONConfiguration jsonConfig = JSONConfiguration.DEFAULT;
final BaseJSONUnmarshaller unmarshaller = new BaseJSONUnmarshaller(context, jsonConfig);
final StringReader reader = new StringReader(ERROR);
final AnObject result = unmarshaller.unmarshalFromJSON(reader, AnObject.class);
}
#XmlRootElement
public static class AnObject {
private Object additionalObject;
public Object getAdditionalObject() {
return additionalObject;
}
public void setAdditionalObject(final Object additionalObject) {
this.additionalObject = additionalObject;
}
}
}
Question:
How could this be solved in general e.g. by some configuration of glassfish to avoid this issue in the first place?
Currently we are working with glassfish 3.1.2.2. Any help is much appreciated!
Related
I have the following camel route:
from(inputDirectory)
.unmarshal(jaxb)
.process(jaxb2CSVDataProcessor)
.split(body()) //because there is a list of CSVRecords
.marshal(bindyCsvDataFormat)
.to(outputDirectory); //appending to existing file using "?autoCreate=true&fileExist=Append"
for my CSV model class I am using annotations:
#CsvRecord(separator = ",", generateHeaderColumns = true)
...
and for properties
#DataField(pos = 0)
...
My problem is that the headers are appended every time a new csv record is appended.
Is there a non-dirty way to control this? Am I missing anything here?
I made a work around which is working quite nicely, creating the header by querying the columnames of the #DataField annotation. This is happening once the first time the file is written. I wrote down the whole solution here:
How to generate a Flat file with header and footer using Camel Bindy
I ended up adding a processor that checks if the csv file exists just before the "to" clause. In there I do a manipulation of the byte array and remove the headers.
Hope this helps anyone else. I needed to do something similar where after my first split message I wanted to supress the header output. Here is a complete class (the 'FieldUtils' is part of the apache commons lib)
package com.routes;
import java.io.OutputStream;
import org.apache.camel.Exchange;
import org.apache.camel.dataformat.bindy.BindyAbstractFactory;
import org.apache.camel.dataformat.bindy.BindyCsvFactory;
import org.apache.camel.dataformat.bindy.BindyFactory;
import org.apache.camel.dataformat.bindy.FormatFactory;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.apache.commons.lang3.reflect.FieldUtils;
public class StreamingBindyCsvDataFormat extends BindyCsvDataFormat {
public StreamingBindyCsvDataFormat(Class<?> type) {
super(type);
}
#Override
public void marshal(Exchange exchange, Object body, OutputStream outputStream) throws Exception {
final StreamingBindyModelFactory factory = (StreamingBindyModelFactory) super.getFactory();
final int splitIndex = exchange.getProperty(Exchange.SPLIT_INDEX, -1, int.class);
final boolean splitComplete = exchange.getProperty(Exchange.SPLIT_COMPLETE, false, boolean.class);
super.marshal(exchange, body, outputStream);
if (splitIndex == 0) {
factory.setGenerateHeaderColumnNames(false); // turn off header generate after first exchange
} else if(splitComplete) {
factory.setGenerateHeaderColumnNames(true); // turn on header generate when split complete
}
}
#Override
protected BindyAbstractFactory createModelFactory(FormatFactory formatFactory) throws Exception {
BindyCsvFactory bindyCsvFactory = new StreamingBindyModelFactory(getClassType());
bindyCsvFactory.setFormatFactory(formatFactory);
return bindyCsvFactory;
}
public class StreamingBindyModelFactory extends BindyCsvFactory implements BindyFactory {
public StreamingBindyModelFactory(Class<?> type) throws Exception {
super(type);
}
public void setGenerateHeaderColumnNames(boolean generateHeaderColumnNames) throws IllegalAccessException {
FieldUtils.writeField(this, "generateHeaderColumnNames", generateHeaderColumnNames, true);
}
}
}
I am using Gson to convert my JSON data to a Map<String, Object>. My JSON has some integer (actually long) fields, and I want them to be parsed as long (obviously). However, Gson parses them as doubles. How can I make Gson parse them as longs/integers? I've seen How to deserialize a list using GSON or another JSON library in Java? but I don't want to create a strongly-typed custom class, I'll be using a Map. I've also seen Android: Gson deserializes Integer as Double and a few other questions which I've thought might be duplicates, but all the answers either point to creating a strongly-typed class, creating extra functions that play role in deserialization or using completely another library.
Isn't there a simple way in Google's own JSON serializer/deserializer that will simply deserialize an integer (yeah, a number without a dot at all) as an integer and not double, as it should have been as default in the first place? If I wanted to send a floating point, I'd be sending 2.0, not 2 from my server JSON. Why on Earth am I getting a double and how do I get rid of it?
UPDATE: Even though I've clearly explained, some people still don't understand the simple fact that I am not in for another library (e.g. Jackson) and I'm aware of the simple fact that any parser should be able to identify 2.0 as a floating-point and 2 as a pure integer and parse accordingly, so please stop pointing me to telling why it's that way because it's simply incorrect and is not an excuse not to parse integers correctly. So, no, this is not a duplicate of Gson. Deserialize integers as integers and not as doubles.
You can't.
Long answer
You can't override gson's built-in numbers converters.
I've made a short code test to peek under the hood which types gson tries to find a delegated converter.
package net.sargue.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.intellij.lang.annotations.Language;
import java.io.IOException;
import java.util.Map;
public class SO36528727 {
public static void main(String[] args) {
#Language("JSON")
String json = "{\n" +
" \"anInteger\": 2,\n" +
" \"aDouble\": 2.0\n" +
"}";
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new LongDeserializerFactory())
.create();
Map<String, Object> m =
gson.fromJson(json,
new TypeToken<Map<String, Object>>() {}.getType());
System.out.println(m.get("aDouble").getClass());
System.out.println(m.get("anInteger").getClass());
}
private static class LongDeserializerFactory
implements TypeAdapterFactory
{
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
System.out.println("type = " + type);
if (type.getRawType().equals(String.class)) {
TypeAdapter<String> stringAdapter =
gson.getDelegateAdapter(this, TypeToken.get(String.class));
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
stringAdapter.write(out, (String) value);
}
#SuppressWarnings("unchecked")
#Override
public T read(JsonReader in) throws IOException {
String s = stringAdapter.read(in);
System.out.println("s = " + s);
return (T) s;
}
};
} else
return null;
}
}
}
The execution result is this:
type = java.util.Map<java.lang.String, java.lang.Object>
type = java.lang.String
s = anInteger
s = aDouble
class java.lang.Double
class java.lang.Double
So, you can see that gson looks just for two converters: the whole Map<> thing and the basic String. But no Double or Integer or Number or even Object. So you CAN'T override it unless you override it from a higher place like when dealing with a Map. And that was answered on the thread you reference on the question.
I produce JSON with a List<> member inside. It is marshalled OK.
However, my consuming (third-)party complains about a missing []-pair, when the list has only one element. What I produce is like:
"mylist":{"id":104,"name":"Only one found"} // produced
while my consumer expects:
"mylist":[{"id":104,"name":"Only one found"}] // expected by third party
Is my implementation producing incorrect JSON?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The JAXB (JSR-222) specification does not cover JSON-binding. The behaviour you are seeing is most likely due to a JAXB implementation being used with a library like Jettison. Jettison converts StAX events to/from JSON and can only detect a list when an element occurs more than once (see: http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html). EclipseLink JAXB offers native JSON binding and can correctly represent arrays of size 1.
JAVA MODEL
Foo
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private List<Bar> mylist;
}
Bar
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Bar {
private int id;
private String name;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
DEMO CODE
Demo
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>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum15404528/input.json");
Foo foo = unmarshaller.unmarshal(json, Foo.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
input.json/Output
We see that the mylist is correctly represented as a JSON array.
{
"mylist" : [ {
"id" : 104,
"name" : "Only one found"
} ]
}
ADDITIONAL INFORMATION
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
I use Jackson to proccess json.
Now, I face a proplem.
My POJO :
class Person{
public String name;
public int age;
}
And the JSON is
{"name":"Jackson","age":""}.
If I write the code like this:
Person person = mapper.readValue("{\"name\":\"Jackson\",\"age\":\"\"}", Person.class);
A Exception is thrown:
Can not construct instance of int from String value "": not a valid int value.
If the JSON is "{\"name\":\"Jackson\",\"age\":null}", it’s OK.
But now , I don’t want to modify the JSON. And how can I do ?
I recommend logging an issue at http://jira.codehaus.org/browse/JACKSON, requesting that this be considered a bug, or that a feature to allow proper handling is added. (Maybe it's reasonable that DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT would also allow deserialization of empty JSON strings to default primitive values, since that's how JSON null values are otherwise handled, when bound to Java primitives.) (Update: I logged issue 616 for this. Vote for it if you want it implemented.)
Until Jackson is so enhanced, custom deserialization processing would be necessary to transform a JSON empty string to a default primitive value (or to whatever non-string value is wanted). Following is such an example, which is fortunately simple, since the existing code to deserialize to an int already handles an empty string, turning it into 0.
import java.io.IOException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.deser.StdDeserializer;
import org.codehaus.jackson.map.module.SimpleModule;
public class Foo
{
public static void main(String[] args) throws Exception
{
// {"name":"Jackson","age":""}
String json = "{\"name\":\"Jackson\",\"age\":\"\"}";
SimpleModule module = new SimpleModule("EmptyJsonStringAsInt", Version.unknownVersion());
module.addDeserializer(int.class, new EmptyJsonStringAsIntDeserializer(int.class));
ObjectMapper mapper = new ObjectMapper().withModule(module);
Person p = mapper.readValue(json, Person.class);
System.out.println(mapper.writeValueAsString(p));
// {"name":"Jackson","age":0}
}
}
class Person
{
public String name;
public int age;
}
class EmptyJsonStringAsIntDeserializer extends StdDeserializer<Integer>
{
protected EmptyJsonStringAsIntDeserializer(Class<?> vc)
{
super(vc);
}
#Override
public Integer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
return super._parseIntPrimitive(jp, ctxt);
}
}
(Also, note that if the target type were Integer instead of int, then the field would be populated with a null value (not that that's what's wanted). For this, I logged issue 617, to request a deserialization configuration to automatically set the primitive default value from a JSON null value, when binding to a primitive wrapper type field. In other words, when deserializing from a JSON null value to an Integer field, the target field would be set to Integer.valueOf(0) instead of null.)
I'm about to develop a JAX-RS based RESTful web service and I use MOXy (JAXB) in order to automatically generate my web service's JSON responses.
Everything is cool, but due to the fact that the web service will be the back-end of a JavaScript-based web application and therefore publicly accessible I don't want to expose certain details like class names, etc.
But, I've realized that under certain conditions MOXy embeds a "#type" entry into the marshalled string and this entry is followed by the class name of the object that has just been marshalled.
In particular, I've realized that MOXy behaves in this way when marshalling instances of extended classes.
Assume the following super class "MyBasicResponse"
#XmlRootElement(name="res")
public class MyBasicResponse {
#XmlElement
private String msg;
public MyBasicResponse() {
// Just for conformity
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
And this specialized (extended) class "MySpecialResponse"
#XmlRootElement(name="res")
public class MySpecialResponse extends MyBasicResponse {
#XmlElement
private String moreInfo;
public MySpecialResponse() {
// Just for conformity
}
public String getMoreInfo() {
return moreInfo;
}
public void setMoreInfo(String moreInfo) {
this.moreInfo = moreInfo;
}
}
So, the MyBasicResponse object's marshalled string is
{"msg":"A Message."}
(That's okay!)
But, the MySpecialResponse object's marshalled string is like
{"#type":"MySpecialResponse","msg":"A Message.","moreInfo":"More Information."}
Is there a way to strip the
"#type":"MySpecialResponse"
out of my response?
You can wrap your object in an instance of JAXBElement specifying the subclass being marshalled to get rid of the type key. Below is a full example.
Java Model
Same as from the question, but with the following package-info class added to specifying the field access to match those classes
#XmlAccessorType(XmlAccessType.FIELD)
package com.example.foo;
import javax.xml.bind.annotation.*;
Demo Code
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
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>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {MySpecialResponse.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
MySpecialResponse msr = new MySpecialResponse();
marshaller.marshal(msr, System.out);
JAXBElement<MySpecialResponse> jaxbElement = new JAXBElement(new QName(""), MySpecialResponse.class, msr);
marshaller.marshal(jaxbElement, System.out);
}
}
Output
We see that when the object was marshalled an type key was marshalled (corresponding to the xsi:type attribute in the XML representation), because as MOXy is concerned it was necessary to distinguish between MyBasicResponse and MySpecialResponse. When we wrapped the object in an instance of JAXBElement and qualified the type MOXy didn't need to add the type key.
{
"type" : "mySpecialResponse"
}
{
}
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html