How to define string name as class in java - jackson

I am using Jackson parser for JSON parsing in android app. The JSON data is in following form
data: {
train_number: "12951",
chart_prepared: false,
class: "2A"
}
How to parse property with class name in Java?
Please, help me.

At the beginning - your JSON is not valid. It should look like this:
{"train_number":1,"chart_prepared":false,"class":"2A"}
You can change default name property using #JsonProperty annotation.
Your POJO class should looks like that:
class Data {
private int train_number;
private boolean chart_prepared;
#JsonProperty(value = "class")
private String clazz;
...
}
Now you can build simple test method:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
Data data = new Data();
data.setTrain_number(1);
data.setClazz("2A");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(data);
System.out.println(json);
System.out.println(objectMapper.readValue(json, Data.class));
}
}
Above program prints:
{"train_number":1,"chart_prepared":false,"class":"2A"}
Data [train_number=1, chart_prepared=false, clazz=2A]

Related

Access JAX-RS resource annotations from a JsonbSerializer

I have an application running on Payara 4 using a custom GSON JSON adapter. I would like to migrate to Payara 5 (5.191) and start using JSON-B. In our current application we can control the JSON output using annotations on a resource.
For example using #Summarize:
#GET
#Path("summary/{encryptedId}")
#Produces(MediaType.APPLICATION_JSON)
#Summarize
public Address findSummarized(#PathParam("encryptedId") String encryptedId) {
return super.find(encryptedId);
}
it will cause a different GSON configuration to be used in our #Provider:
#Provider
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
public GsonProvider() {
gson = getGson(EntityAdapter.class);
gsonSummary = getGson(EntitySummaryAdapter.class);
}
...
#Override
public void writeTo(T object,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
boolean summarize = contains(annotations, Summarize.class);
try (PrintWriter printWriter = new PrintWriter(entityStream)) {
printWriter.write((summarize ? gsonSummary : gson).toJson(object));
printWriter.flush();
}
}
}
I want to do something similar in the new JSON-B setup. I annotated our entities with #JsonbTypeSerializer(MySerializer.class), so I would like to be able to detect from within the serializer what it should do: either create a full serialized JSON object, or a summary.
What I hoped to do is set a property in the JsonbConfig, like so:
JsonbConfig config = new JsonbConfig()
.setProperty("com.myCompany.jsonb.summarize", true);
and read it in the serializer using #Context (just guessing that this might work here), like so:
#Context
private JsonbConfiguration config;
.. but that's not. Is there any way to access JAX-RS resource annotations from a JsonbSerializer?
You could accomplish a similar goal using two separate Jsonb instances in your JAX-RS provider class like so:
#Provider
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class JsonbProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
private static final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
.withAdapters(new EntityAdapter()));
private static final Jsonb jsonbSummary = JsonbBuilder.create(new JsonbConfig()
.withAdapters(new EntitySummaryAdapter()));
...
#Override
public void writeTo(T object,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
boolean summarize = contains(annotations, Summarize.class);
try (PrintWriter printWriter = new PrintWriter(entityStream)) {
printWriter.write((summarize ? jsonbSummary : jsonb).toJson(object));
printWriter.flush();
}
}
}
In the end I opted to create summaries from within my entities and drop the annotation on my REST resources. It was a bit of work, but I think it has been worth it.
I created a Summarizable interface and added a default method there to create a simple map summary of any entity, based on a extended version of the PropertyVisibilityStrategy we created for the full version of the entities.
public interface Summarizable {
public default Map<String, Object> toSummary() {
SummaryPropertyVisibilityStrategy summaryStrategy = new SummaryPropertyVisibilityStrategy();
Map<String, Object> summary = new LinkedHashMap<>();
ReflectionUtils.getFields(this.getClass())
.stream()
.filter(summaryStrategy::isVisible)
.map(f -> new AbstractMap.SimpleEntry<>(f.getName(), summarize(f)))
.filter(e -> e.getValue() != null)
.forEach(e -> summary.put(e.getKey(), e.getValue()));
return summary;
}
public default Object summarize(final Field field) {
Object value = ReflectionUtils.getValueJsonb(this, field);
return value != null && Stream.of(ManyToOne.class, OneToOne.class).anyMatch(field::isAnnotationPresent)
? value.toString()
: value;
}
}
public static Object getValueJsonb(final Object object, final Field field) {
field.setAccessible(true);
JsonbTypeAdapter adapterAnnotation = field.getAnnotation(JsonbTypeAdapter.class);
try {
Object value = field.get(object);
return adapterAnnotation == null
? value
: adapterAnnotation.value().newInstance().adaptToJson(value);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}

Jackson - mapping OffsetDateTime [duplicate]

I have problems LocalDateTime deserialization in Junit test. I have simple REST API which returns some DTO object. When I call my endpoint there is no problem with response - it is correct. Then I try to write unit test, obtain MvcResult and with use of ObjectMapper convert it to my DTO object. But I still receive:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.time.LocalDateTime` out of START_ARRAY token
at [Source: (String)"{"name":"Test name","firstDate":[2019,3,11,18,34,43,52217600],"secondDate":[2019,3,11,19,34,43,54219000]}"; line: 1, column: 33] (through reference chain: com.mylocaldatetimeexample.MyDto["firstDate"])
I was trying with #JsonFormat and adding compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.9.8' to my build.gradle but I use Spring Boot 2.1.3.RELEASE so it is involved in it. I do not have any idea how to fix it. My simple endpoint and unit test below:
#RestController
#RequestMapping("/api/myexample")
public class MyController {
#GetMapping("{id}")
public ResponseEntity<MyDto> findById(#PathVariable Long id) {
MyDto myDto = new MyDto("Test name", LocalDateTime.now(), LocalDateTime.now().plusHours(1));
return ResponseEntity.ok(myDto);
}
}
MyDto class
public class MyDto {
private String name;
private LocalDateTime firstDate;
private LocalDateTime secondDate;
// constructors, getters, setters
}
Unit test
public class MyControllerTest {
#Test
public void getMethod() throws Exception {
MyController controller = new MyController();
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/api/myexample/1"))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
String json = mvcResult.getResponse().getContentAsString();
MyDto dto = new ObjectMapper().readValue(json, MyDto.class);
assertEquals("name", dto.getName());
}
}
You create new ObjectMapper in test class:
MyDto dto = new ObjectMapper().readValue(json, MyDto.class);
Try to inject ObjectMapper from Spring context or manually register module:
mapper.registerModule(new JavaTimeModule());
See also:
jackson-modules-java8

Immutables wrapper as string in Jackson

How can I jackson serialize a wrapper type to and from a string?
I merged the following from two different examples their website. But the HostName type is serialized/deserialized as
{ "name" : "my.host.name.com" }
when I want it to be simply the string
"my.host.name.com"
Note that I have a lot of XName types, hence the use of the Immutables wrapper. So I would prefer a solution that keeps the amount of boiler plate down.
#Value.Immutable #AbstractName.Wrapper
public abstract class _HostName extends AbstractName { }
...
public abstract class AbstractName {
#JsonSerialize
#JsonDeserialize
#Value.Style(
// Detect names starting with underscore
typeAbstract = "_*",
// Generate without any suffix, just raw detected name
typeImmutable = "*",
// Make generated public, leave underscored as package private
visibility = Value.Style.ImplementationVisibility.PUBLIC,
// Seems unnecessary to have builder or superfluous copy method
defaults = #Value.Immutable(builder = false, copy = false))
public #interface Wrapper {}
#Value.Parameter
public abstract String name();
#Override
public String toString() { return name(); }
}
I've got this to work like below. There's an extra annotation on my name types. It's not my favorite, but it works.
#JsonDeserialize(as=HostName.class)
#Value.Immutable #AbstractName.Wrapper
public abstract class _HostName extends AbstractName { }
...
public abstract class AbstractName {
#Value.Style(
// Detect names starting with underscore
typeAbstract = "_*",
// Generate without any suffix, just raw detected name
typeImmutable = "*",
// Make generated public, leave underscored as package private
visibility = Value.Style.ImplementationVisibility.PUBLIC,
// Seems unnecessary to have builder or superfluous copy method
defaults = #Value.Immutable(builder = false, copy = false))
public #interface Wrapper {}
#JsonValue
#Value.Parameter
public abstract String name();
#Override
public String toString() { return name(); }
}
Here's a little program to run it:
public static void main(String... args) throws IOException {
ObjectMapper json = new ObjectMapper();
String text = json.writeValueAsString(HostName.of("my.host.name.com"));
System.out.println(text);
HostName hostName = json.readValue(text, HostName.class);
System.out.println(hostName);
}

Jackson Included.NON_DEFAULT not working in 2.7.0

I'm trying to upgrade from Jackson 2.3 to Jackson 2.7 , and it appears the #JsonIgnore(Include.NON_DEFAULT) behavior has changed.
With the following code
package jackson;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
public static void main (String args[]) throws Exception {
MyClass foo = new MyClass();
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(foo));
}
public static enum MyEnum { A, B, C };
public static class MyClass {
public String name = "John";
#JsonInclude(Include.NON_DEFAULT)
public MyEnum myEnum = MyEnum.A;
}
}
I get this output using 2.7:
{"name":"John","myEnum":"A"}
and the following using 2.3:
{"name":"John"}
How do I replicate the behavior in 2.3 using version 2.7 ?
According to the bug ticket I put in for this, it is working as intended and that functionality can be achieved by structuring your class as:
#JsonInclude(JsonInclude.Include.NON_DEFAULT)
public static class MyClass {
#JsonInclude(JsonInclude.Include.ALWAYS)
public String name = "John";
public MyEnum myEnum = MyEnum.A;
}
See the ticket for a more thorough explanation from the author.

Morphia Interface for List of enum does not work (unmarshalling)

I have the following interface
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "className")
public interface InfoChartInformation {
public String name();
}
And the following implementation (enum):
public class InfoChartSummary {
public static enum Immobilien implements InfoChartInformation {
CITY, CONSTRUCTION_DATE;
}
public static enum Cars implements InfoChartInformation {
POWER, MILEAGE;
}
}
Then I use all of It in the following entity:
#Entity(noClassnameStored = true)
#Converters(InfoChartInformationMorphiaConverter.class)
public class TestEntity{
#Id
public ObjectId id;
#Embedded
public List<InfoChartInformation> order;
}
Jackson, in order to detect the type on the unmarshalling time, will add to every enum on the list the className.
I thought morphia would do the same, but there's no field className in the List of enum and the unmarshalling cannot be done correctly: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassCastException: java.lang.String cannot be cast to com.mongodb
.DBObject
I guess the correct behavior should be to save all the enum route (package+name), not only the enum name. At least in that way the unmarshalling could be performed. There's a way morphia supports that by default or I need to create my own converter (similar to this) ?
I tried creating a Custom Converter:
public class InfoChartInformationMorphiaConverter extends TypeConverter{
public InfoChartInformationMorphiaConverter() {
super(InfoChartInformation.class);
}
#Override
public Object decode(Class targetClass, Object fromDBObject, MappedField optionalExtraInfo) {
if (fromDBObject == null) {
return null;
}
String clazz = fromDBObject.toString().substring(0, fromDBObject.toString().lastIndexOf("."));
String value = fromDBObject.toString().substring(fromDBObject.toString().lastIndexOf(".") + 1);
try {
return Enum.valueOf((Class)Class.forName(clazz), value);
} catch (ClassNotFoundException e) {
return null;
}
}
#Override
public Object encode(final Object value, final MappedField optionalExtraInfo) {
return value.getClass().getName() + "." + ((InfoChartInformation) value).name();
}
}
Then, I added the converter information to morphia morphia.getMapper().getConverters().addConverter(new InfoChartInformationMorphiaConverter());.
However, when serializing (or marshalling) the object to save it into the database, the custom converter is ignored and the Enum is saved using the default Morphia converter (only the enum name).
If I use in the TestEntity class only an attribute InfoChartInformation; instead of the List<>InfoChartInformation>, my customer converter will work. However I need support for List
Use:
public class InfoChartInformationMorphiaConverter extends TypeConverter implements SimpleValueConverter
It is a marker interface required to make your Convertor work.