Display XML correctly in Firefox with Jackson - java

I'm converting a JSON file into an ArrayList and then to XML by using Jackson. It is displayed in Firefox but just as a normal String. By using the inspect element tool I get the whole formatted xml though. Which function can I use to display it correctly on the browser?
My method:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
}
Desired output in browser:
<myclass xmlns="">
<myclass>XyClass</ci>
<myname>XyName</ci>
...
</myclass>
Actual output:
XyClassXyName...
Quite simplified the class looks like this:
#JacksonXmlRootElement(localName ="MyClass")
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class MyClass {
#XmlElement(required = true)
private String class;
#XmlElement(required = true)
private String name;
//....
//standard constructor
public MyClass() { }
public CI(String class, String name){
this.class = class;
this.name = name;
}
public String getClass() {
return class;
}
public String getName() {
return name;
}
public void setClass(String class) {
this.class = class;
}
public void setName(String name) {
this.name = name;
}
}
Another weird thing is that I have the exact annotations in another class, trying the same thing with that and there the browser does not display anything... Thanks for any help.

Well my mistake derived from two things basically. Most important is the definiton of the XML Root Element (not only as annotation in your "MyClass"). Define a global String to create a XML Root element, otherwise your document won't be well-formed and the mistake "junk after document element" will be shown.
private String asXml ="<?xml version=\"1.0\" encoding=\"utf-8\"?><MyList>";
I've adapted the method to:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName("MyClass").writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
asXml += "</MyList>";
}
And don't forget to add the correct MediaType in your RestController:
#RequestMapping(value="/display", method=RequestMethod.GET, produces=MediaType.APPLICATION_XML_VALUE)
public #ResponseBody String getList(Model model) {
return service.getAsXmlString();
}

Related

Jackson ObjectMapper - How to exclude field when value is false after de-serialized from remote response?

I am making a remote call to external service A which would return the response as json and then using object mapper to de-serialize into MyResponse object. After that in my current service, I need to attach this object and output to the UI.
One of the field in MyResponse from service A is a boolean and I only want my UI response to include this field when the value is true. Note that I don't have access to modify my MyResponse object as it was read-only. So I created a MixIn class also tried couple of ways but it didn't work..
public class MyResponse {
private String stringValue;
private int intValue;
// Expectation: only include this field when value true, and exclude it when value is false
private boolean booleanValue;
}
// #JsonIgnoreProperties(value = { "booleanValue" })
// #JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
private static class MixInMyResponse {
}
// this would be my rest service eventually send myResponse to UI
public MyResponse readFromRemote() throws IOException {
String jsonAsString =
"{\"stringValue\":\"a\",\"intValue\":1,\"booleanValue\":false}";
ObjectMapper mapper = new ObjectMapper();
// configure object mapper with mix in
mapper.getDeserializationConfig().addMixInAnnotations(MyResponse.class, MixInMyResponse.class);
MyResponse myResponse = mapper.readValue(jsonAsString, MyResponse.class);
// Expectation: writeValue needs only include booleanValue when value true, and exclude booleanValue when value is false
String writeValue = mapper.writeValueAsString(myResponse);
System.out.println(writeValue);
return myResponse;
}
Using #JsonIgnoreProperties(value = { "booleanValue" }): this would do the trick when value is false, but also it doesn't include the field when value is true
Using #JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY): when value is false, this would deserialized field booleanValue as false, so my returned myResponse/writeValue will still have this field to UI.
Is there any additional suggestion on that?
Write your own Serializer
public class MyResponseSerializer extends JsonSerializer<MyResponse> {
#Override
public void serialize(MyResponse myResponse, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("stringValue", myResponse.getStringValue());
jsonGenerator.writeNumberField("intValue", myResponse.getIntValue());
if (myResponse.isBooleanValue()) {
jsonGenerator.writeBooleanField("booleanValue", myResponse.isBooleanValue());
}
jsonGenerator.writeEndObject();
}
}
Register it with the ObjectMapper
#Test
public void test3() throws IOException {
MyResponse response1 = new MyResponse("response1", 1, true);
MyResponse response2 = new MyResponse("response2", 1, false);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(MyResponse.class, new MyResponseSerializer());
objectMapper.registerModule(module);
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response1));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response2));
}
See this tutorial
I edited the original answer when I saw you couldn't modify MyResponse to add an annotation.
Well you can do something like this of course.
First of all, your booleanValue field should be boxed type Boolean not premitive type boolean. Because primitive type boolean default value is false.
You can crate a setter method for booleanValue and annotate it with #JsonProperty("booleanValue") like following.
public class MyResponse {
private String stringValue;
private int intValue;
private Boolean booleanValue;
#JsonProperty("booleanValue")
public void anyNameYouWant(Boolean b) {
if(b) this.booleanValue = true;
}
// getters and setters
}
Finally you can do the following,
String s = "{\"stringValue\":\"a\",\"intValue\":1,\"booleanValue\":false}";
ObjectMapper om = new ObjectMapper();
om.readValue(s, MyResponse.class);

JaxbDto Serialization and deserialization

I need to receive some message with SOAP so I've generated a few classes by xsd-scheme and maven-jaxb2-plugin like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Claim", propOrder = {
"field",
})
public class ClaimType {
#XmlElement(required = true, type = Integer.class, nillable = false)
protected Integer field;
public Integer getField() {
return bpType;
}
public void setField(Integer value) {
this.field= value;
}
}
After receiving message I need to send these to the next one microservice in wrap of HashMap.
I supposed to use ObjectMapper to convert:
//JAXB DTO --> JSON
ObjectMapper objectMapper = new ObjectMapper();
String jsonContent = objectMapper.writeValueAsString(claimType);
map.put("json", jsonContent);
//JSON --> JAXB DTO
ObjectMapper objectMapper = new ObjectMapper();
String json = map.get("json");
ClaimType claimType = objectMapper.readValue(json, ClaimType.class);
But the generated classes are haven't any constructors so I got the exception like "
No creator like default constructor are exists".
What is the best preactice to work with Jaxb Dto? Can I do smth to successful convert these json to object? Thanks in advance!
I've solved my problem by using ObjectMapper MixIn:
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
#JsonIgnoreProperties(value = {"globalScope", "typeSubstituted", "nil"})
public abstract class JAXBElementMixIn<T> {
#JsonCreator
public JAXBElementMixIn(#JsonProperty("name") QName name,
#JsonProperty("declaredType") Class<T> declaredType,
#JsonProperty("scope") Class scope,
#JsonProperty("value") T value) {
}
}
And the convertation:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(JAXBElement.class, JAXBElementMixIn.class);
solution link

How to deserialize a blank JSON string value to null for java.lang.String?

I am trying a simple JSON to de-serialize in to java object. I am however, getting empty String values for java.lang.String property values. In rest of the properties, blank values are converting to null values(which is what I want).
My JSON and related Java class are listed below.
JSON string:
{
"eventId" : 1,
"title" : "sample event",
"location" : ""
}
EventBean class POJO:
public class EventBean {
public Long eventId;
public String title;
public String location;
}
My main class code:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
try {
File file = new File(JsonTest.class.getClassLoader().getResource("event.txt").getFile());
JsonNode root = mapper.readTree(file);
// find out the applicationId
EventBean e = mapper.treeToValue(root, EventBean.class);
System.out.println("It is " + e.location);
}
I was expecting print "It is null". Instead, I am getting "It is ". Obviously, Jackson is not treating blank String values as NULL while converting to my String object type.
I read somewhere that it is expected. However, this is something I want to avoid for java.lang.String too. Is there a simple way?
Jackson will give you null for other objects, but for String it will give empty String.
But you can use a Custom JsonDeserializer to do this:
class CustomDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.readValueAsTree();
if (node.asText().isEmpty()) {
return null;
}
return node.toString();
}
}
In class you have to use it for location field:
class EventBean {
public Long eventId;
public String title;
#JsonDeserialize(using = CustomDeserializer.class)
public String location;
}
It is possible to define a custom deserializer for the String type, overriding the standard String deserializer:
this.mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
});
mapper.registerModule(module);
This way all String fields will behave the same way.
You might first like to see if there has been any progress on the Github issue requesting this exact feature.
For those using Spring Boot: The answer from jgesser was the most helpful to me, but I spent a while trying to work out the best way to configure it in Spring Boot.
Actually, the documentation says:
Any beans of type com.fasterxml.jackson.databind.Module are
automatically registered with the auto-configured
Jackson2ObjectMapperBuilder and are applied to any ObjectMapper
instances that it creates.
So here's jgesser's answer expanded into something you can copy-paste into a new class in a Spring Boot application
#Configuration
public class EmptyStringAsNullJacksonConfiguration {
#Bean
SimpleModule emptyStringAsNullModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(
String.class,
new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser parser, DeserializationContext context)
throws IOException {
String result = StringDeserializer.instance.deserialize(parser, context);
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
});
return module;
}
}
I could get this by following configuration.
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
it is possible to use JsonCreator annotation. It worked for me
public class Foo {
private String field;
#JsonCreator
public Foo(
#JsonProrerty("field") String field) {
this.field = StringUtils.EMPTY.equals(field) ? null : field ;
}
}

Jackson object mapper includes escape sequence in responses

When I use object mapper, it inluces \r\n in the responses.Help me how to resolve it.
I am having train POJO and it has String name and String Value.
I set name as "Sydney" and Value as "SYD".It reruns
{\ \ \"name \" : \"Sydney\",\ \ \"Value \" : \"SYD\",\ \ \"isEnable\" : false,\ \ \"isCurrent\" : false\ \ }"
raw value in browser
"{\r\n \"name\" : \"Sydney\",\r\n \"name\" : \"SYD\",\r\n \"isEnable\" : false,\r\n \"isCurrent\" : false\r\n}"
below is my code
Train
public class Train {
public Train() {
}
private String name;
private String value;
private String Code;
private String countryName;
private String state;
private String stateName;
private boolean isEnable;
private boolean isCurrent;
//*getters and setters/*/
}
Controller calss
public ResponseEntity<String> getDetails( )
throws IOException {
ResponseEntity<String> responseEntity = null;
try(StringWriter writer = new StringWriter()) {
ObjectMapper mapper = new ObjectMapper();
Train train = new Train();
// set name and value to the train object//
if(train != null)
{
mapper.setSerializationInclusion(Inclusion.NON_NULL);
mapper.setSerializationInclusion(Inclusion.NON_EMPTY);
mapper.writerWithDefaultPrettyPrinter().writeValue(writer,
train);
responseEntity = new ResponseEntity<>(writer.toString(),
HttpStatus.OK);
}
}
catch()
{}
return responseEntity;
}
Configuration:
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
converters.add(extendedJsonConvertor());
super.configureMessageConverters(converters);
}
#Bean
public MappingJackson2HttpMessageConverter extendedJsonConvertor() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter
.setObjectMapper(getNullAndEmptyFilteredObjectMapper());
return mappingJackson2HttpMessageConverter;
}
#Bean
public ObjectMapper getNullAndEmptyFilteredObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
When I debug the above code I came to know mapper include those \r\n in the response.Help me how to remove those slashes.
The problem is the line below.
mapper.writerWithDefaultPrettyPrinter().writeValue(writer,train);
Try replacing it with
mapper.writeValue(writer,train);
Why do you create a new object mapper when you are configuring, MappingJackson2HttpMessageConverter?
You can autowire the object mapper or return the actual object and let spring convert it to json
That's a "simple" double encoding issue i believe. You set a string in the response entity which is again writen as a json response.
If you want to rely on the spring view rendering (mappingjackson2httpmessageconverter) you have to create a response entity for "Train". (Or return a train instance directly from your controller method)
Or you use the way you implemented it and you have to ensure that rendering a string for a json response will not use the jackson message converter, but is left untouched by spring.

JAXB null instead empty string during marshaling

How I can print 'null' as field value, when marshalling the string?
Example: error and error_code are Strings, and i want to use 'null' as a value indicating that there is no value/errors happened on the server side.
{
"error_code": null,
"error": null
}
Today, I have to use EMPTY values, so that "error_code" or "error" these fields generally fall into json, and if they were not explicitly initialized as this.errorCode = StringUtils.EMPTY;
So today, I have next json:
{
"error_code": "",
"error": ""
}
This is how that looks in a code:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Response
{
#SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(Response.class);
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME)
private String errorCode;
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_FIELD_NAME)
private String errorMessage;
// Empty Constructor
public Response()
{
this.errorCode = StringUtils.EMPTY; // explicit initialization, otherwise error_code will not appear as part of json, how to fix this this ?
this.errorMessage = StringUtils.EMPTY;
}
etc...
// Empty Constructor
public Response()
{
this.errorCode = null; // this variant dosn't work either, and error_code again didn't get to json
this.errorMessage = null;
}
See, #XmlJavaTypeAdapter, i thought that this potentially could help me - but no :)
Instead of null value, i'm getting "null" as string.
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
{
"error_code": "null", // this is not whta i wanted to get.
"error": "null"
}
Any help on this? - ask me if something is not clear.
full list:
/**
* Empty string Adapter specifying how we want to represent empty strings
* (if string is empty - treat it as null during marhsaling)
*
*/
#SuppressWarnings("unused")
private static class EmptyStringAdapter extends XmlAdapter<String, String>
{
#Override
public String unmarshal(String str) throws Exception
{
return str;
}
#Override
public String marshal(String str) throws Exception
{
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could use MOXy as your JSON provider to support this use case. Below is an example:
Response
MOXy will marshal properties marked with #XmlElement(nillable=true) to the representation you are looking for
(see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).
package forum11319741;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME, nillable = true)
private String errorCode;
#XmlElement(name = Response.ERROR_FIELD_NAME, nillable = true)
private String errorMessage;
}
jaxb.properties
To use 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
package forum11319741;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
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);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
Output
{
"error_code" : null,
"error" : null
}
MOXy and JAX-RS
You can use the MOXyJsonProvider class to enable MOXy as your JSON provider in your JAX-RS application (see: http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html).
package org.example;
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class CustomerApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(CustomerService.class);
return set;
}
}
For More Information
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html

Categories

Resources