How to generate JSON schema from a JAXB annotated class? - json

I have a entity class looks like this.
#XmlRootElement
public class ImageSuffix {
#XmlAttribute
private boolean canRead;
#XmlAttribute
private boolean canWrite;
#XmlValue;
private String value;
}
And I'm using following dependency for JSON generation.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.1.4</version>
</dependency>
When I tried with following code, (which referred from Generating JSON Schemas with Jackson)
#Path("/imageSuffix.jsd")
public class ImageSuffixJsdResource {
#GET
#Produces({MediaType.APPLICATION_JSON})
public String read() throws JsonMappingException {
final ObjectMapper objectMapper = new ObjectMapper();
final JsonSchema jsonSchema =
objectMapper.generateJsonSchema(ImageSuffix.class);
final String jsonSchemaString = jsonSchema.toString();
return jsonSchemaString;
}
}
Server complains with following error message
java.lang.IllegalArgumentException: Class com.googlecode.jinahya.test.ImageSuffix would not be serialized as a JSON object and therefore has no schema
at org.codehaus.jackson.map.ser.StdSerializerProvider.generateJsonSchema(StdSerializerProvider.java:299)
at org.codehaus.jackson.map.ObjectMapper.generateJsonSchema(ObjectMapper.java:2527)
at org.codehaus.jackson.map.ObjectMapper.generateJsonSchema(ObjectMapper.java:2513)
How can I fix this?

Have you tried configuring your ObjectMapper to include jaxb introspector? We use spring mvc3 for implementing REST services and use the same model objects to serialize into xml/json.
AnnotationIntrospector introspector =
new Pair(new JaxbAnnotationIntrospector(), new JacksonAnnotationIntrospector());
objectMapper.setAnnotationIntrospector(introspector);
objectMapper.generateJsonSchema(ImageSuffix.class);
EDIT: Here is the output I get from jackson:
{
"type" : "object",
"properties" : {
"canRead" : {
"type" : "boolean",
"required" : true
},
"canWrite" : {
"type" : "boolean",
"required" : true
},
"value" : {
"type" : "string"
}
}
}
Hope this helps!

The provided answer is a bit old and some of the things have been deprecated now. So try the following code with the latest Jackson and JAXB/Moxy annotated classes:
Approach-1
class JsonSchemaGenerator{
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = TypeFactory.defaultInstance();
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
objectMapper.getDeserializationConfig().with(introspector);
objectMapper.getSerializationConfig().with(introspector);
//To force mapper to include JAXB annotated properties in Json schema
objectMapper.registerModule(new JaxbAnnotationModule());
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
objectMapper.acceptJsonFormatVisitor(objectMapper.constructType(Customer.class), visitor);
JsonSchema inputSchema = visitor.finalSchema();
String schemaString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inputSchema);
System.out.println(schemaString);
}
}
Approach -2 :
class JsonSchemaGenerator{
public static void main(String[] args) throws JsonProcessingException, ClassNotFoundException {
final ObjectMapper mapper = new ObjectMapper();
final TypeFactory typeFactory = TypeFactory.defaultInstance();
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
mapper.getDeserializationConfig().with(introspector);
mapper.getSerializationConfig().with(introspector);
final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName("com.jaxb.Customer"));
System.out.println(jsonSchema);
}
}

Related

JSON conversion to a specific format

I have a Java Object and trying to convert this to a specific JSON format
public class Audit {
String auditId;
String auditData;
}
and above object needs to convert to below JSON format
{
"event":"auditId=100,auditData=purchase order"
}
how do we convert above format using Jackson parser
You need to write a custom serializer for Audit class.
public class AuditSerializer extends StdSerializer<Audit> {
public AuditSerializer() {
super(Audit.class);
}
protected AuditSerializer(Class<Audit> auditClass) {
super(auditClass);
}
#Override
public void serialize(Audit audit, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
String key = "event";
String value = String.format("auditId=%s,auditData=%s", audit.auditId, audit.auditData);
gen.writeStringField(key, value);
gen.writeEndObject();
}
}
And then use this custom serializer to get JSON string for Audit:
SimpleModule module = new SimpleModule();
module.addSerializer(new AuditSerializer(Audit.class));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Audit audit = new Audit();
audit.auditId = "100";
audit.auditData = "purchase Order";
System.out.println(mapper.writeValueAsString(audit));
// {"event":"auditId=100,auditData=purchase Order"}
If you annotate Audit class with #JsonSerialize(using=AuditSerializer.class), then you do not need to explicitly register AuditSerializer.
#JsonSerialize(using=AuditSerializer.class)
public class Audit {
...
And you use ObjectMapper directly.
Audit audit = new Audit();
audit.auditId = "100";
audit.auditData = "purchase Order";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(audit));
// {"event":"auditId=100,auditData=purchase Order"}

How to bypass #JsonIgnore annotation?

I am trying to create a rest end point and using Swagger as UI representation. The pojo which I'm using it has a variable annotated with #JsonIgnore as shown below.
#JsonIgnore
private Map<String, Object> property = new HashMap<String, Object>();
Now, when I'm providing JSON (with property value) to this end point and trying to read its value it is coming out as null (due to #JsonIgnore).
pojoObj.getProperties(); //null
Is there any way if I can get property value without removing the #JsonIgnore annotation?
This can be achieved by utilizing Jackson's Mixin feature, where you create another class that cancels the ignore annotation. You can then "attach" the mixin to the ObjectMapper at run time:
This is the POJO I used:
public class Bean
{
// always deserialized
public String name;
// ignored (unless...)
#JsonIgnore
public Map<String, Object> properties = new HashMap<String, Object>();
}
This is the "other" class. It is just another POJO with the same property name
public class DoNotIgnore
{
#JsonIgnore(false)
public Map<String, Object> properties;
}
a Jackson Module is used to tie the bean to the mixin:
#SuppressWarnings("serial")
public class DoNotIgnoreModule extends SimpleModule
{
public DoNotIgnoreModule() {
super("DoNotIgnoreModule");
}
#Override
public void setupModule(SetupContext context)
{
context.setMixInAnnotations(Bean.class, DoNotIgnore.class);
}
}
Tying it all together:
public static void main(String[] args)
{
String json = "{\"name\": \"MyName\","
+"\"properties\": {\"key1\": \"val1\", \"key2\": \"val2\", \"key3\": \"val3\"}"
+ "}";
try {
ObjectMapper mapper = new ObjectMapper();
// decide at run time whether to ignore properties or not
if ("do-not-ignore".equals(args[0])) {
mapper.registerModule(new DoNotIgnoreModule());
}
Bean bean = mapper.readValue(json, Bean.class);
System.out.println(" Name: " + bean.name + ", properties " + bean.properties);
} catch (IOException e) {
e.printStackTrace();
}
}

How to use Jackson Annotations to process JSON with random object names

I have the following JSON:
{
"animals": {
"113110": {
"id": 113110,
"name": "Dog",
.....
},
"121853": {
"id": 121853,
"name": "Cat",
.....
}
}
}
Ideally, the JSON should be as follows and implementing Jackson annotations will be trivial:
{
"animals": [
{
"id": 113110,
"name": "Dog",
.....
},
{
"id": 121853,
"name": "Cat",
.....
}
]
}
However, is there a way to use Jackson to abstract the object names so I can work with the original JSON, if anybody gets my meaning?
EDIT:
I do not know how to create my POJO. I could create an Animal class, with objects 113110 and 121853, but as these objects will always vary, how do I use Jackson annotations in my Animal class so that I can deserialize the JSON?
Thanks all, but I couldn't really understand the rest of the answers ( I don't really want to delve into Jackson, I just want to convert it to a POJO), so I found an alternative solution.
I left out a key bit of information: The JSON I posted is part of a much larger JSON object.
I ended up using Jackson's #AnySetter as I noticed that any "un-parsable" JSON data related to "animals" could be retrieved in additionalProperties defined as follows in its parent class:
public class AnimalParent {
#JsonIgnore
private Animal animal;
#JsonIgnore
private Map<String, Object> additionalProperties =
new HashMap<String, Object>();
public Animal getAnimal() {
return this.animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Then in my main method where I parse the parent JSON, I have the following after the parsing is completed to parse the animals.
// start parsing parent JSON
...
// end parsing parent JSON
// parse animal
ObjectMapper mapper = new ObjectMapper();
if (animalParent.getAdditionalProperties() != null) {
for (Map.Entry<String, Object> item : animalParent
.getAdditionalProperties().entrySet()) {
Animal animal = mapper.convertValue(item.getValue(), Animal.class);
animalParent.setAnimal(animal);
}
It is always a bit messy when your JSON is "dynamic" as per the OPs example. The main approaches are
parse the JSON to some kind of dynamic Map-structure
parse the JSON to a tree-structure (i.e. JsonNode)
use a custom deserializer to parse the JSON and map it to a POJO
There are downsides to all of these approaches. The Map-approach offers no type safety and does not offer much functionality when it comes to traversing the object structure.
The JsonNode approach offers some nice type-methods and also some traversal methods. IMO this is a cleaner approach than the Map-approach.
The POJO-approach is type safe but a custom deserializer is required which is generally not pretty...
So, maybe the following "hybrid" approach can be of use.
// Setup the mapper
final ObjectMapper mapper = new ObjectMapper();
// Parse the json to a tree (JsonNode). This is IMO nicer than the
// Map since it exposes some nice methods for managing the
// underlying data
final JsonNode json = mapper.readTree(jsonString);
// Alt 1, use JsonNode directly
for (final JsonNode animal : json.path("animals")) {
final int id = animal.get("id").asInt();
final String name = animal.get("name").asText();
// Do stuff with name and id...
}
If the JsonNode approach feels a bit too raw then it is possible to convert the JsonNode object to a POJO without the use of a deserializer. If you assume the following POJO:
public class Animal {
private final int id;
private final String name;
#JsonCreator
public Animal(#JsonProperty("id") final int id, #JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
Then, this code can be used for converting to POJOs:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(jsonString);
// Alt 2, convert to a Pojo
for (final JsonNode animal : json.path("animals")) {
final Animal a = mapper.treeToValue(animal, Animal.class);
// Handle the animal instance...
}
Finally, if the POJO still contains dynamic data you can use the following approach to handle that. In your POJO, declare the following:
private final Map<String, Object> dynamic = new HashMap<>();
#JsonAnySetter
private void set(String name, Object value) {
dynamic.put(name, value);
}
Note that it is not a must for the method to be public (i.e. it can be hidden from the outside world). This way you'll get hold of all the unknown/dynamic JSON elements.
Personally, any time I'm dealing with weird JSON that doesn't map easily to POJOs, I just do custom serialization.
I would probably make the POJOs look something like this:
public class Animal
{
String id;
String name;
}
public class JsonThing
{
List<Animal> animals;
}
Then I would implement a custom parser using the Jackson stream API. Here's a quick stub of a JsonDeserializer<JsonThing>:
public Stuff deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
.... // Start by creating a JsonThing instance and init the list.
while (jp.nextToken() != JsonToken.END_OBJECT)
{
jp.nextToken();
switch (jp.getCurrentName())
{
case "animals":
jp.nextToken(); // Skip to {
jp.nextToken(); // Skip id field
Animal a = jp.readValuesAs(Animal.class);
// Add to list
}
}
..... // Return JsonThing
}
If the keys are not known in advance then use Map instead of POJO.
Have a look at Example 1 and Example 2
You can try any one.
sample code: (using Jackson Library)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
sample code: using GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
Perhaps it's just a question of combining simple Maps with pojos? Like:
public class Wrapper {
public Map<Long, Animal> animals;
}
public class Animal {
public long id;
public String name;
}
and that's it; although ids there match, maybe there is no need to try to model that dependency.

include class name in all objects serialized by jackson

How to include class name in all serialized objects? E.g. adding "_class: 'MyClass'" to output value. Is there some global setting for that? I don't want to add any annotation to pojo classes.
I'm using it with spring4 webmvc #ResponseBody (only json format).
You need to annotated your type with the #JsonTypeInfo annotation and configure how the type information should be serialized. Refer this page for reference.
Example:
public class JacksonClassInfo {
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "__class")
public static class Bean {
public final String field;
#JsonCreator
public Bean(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String toString() {
return "Bean{" +
"field='" + field + '\'' +
'}';
}
}
public static void main(String[] args) throws IOException {
Bean bean = new Bean("value");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
System.out.println(json);
System.out.println(mapper.readValue(json, Bean.class));
}
}
Output:
{
"__class" : "stackoverflow.JacksonClassInfo$Bean",
"field" : "value"
}
Bean{field='value'}

Telling Retrofit to what variable it should map a certain json field?

The REST API I'm talking to is responding to some of the requests in a structure as such:
{
"_links": {
"next": "NEXT_DATA_BLOCK_URL",
"prev": "PREV_DATA_BLOCK_URL",
"self": "CURRENT_DATA_BLOCK_URL"
},
"RESPONSE_DATA_NAME": [
{
... DATA_FIELDS ...
}
]
}
Where 'RESPONSE_DATA_NAME' is the data "name" - changes according to desired request. for example, it might be 'teams' or 'messages'.
Therefore I created a generic class with the following members:
public class PagedResponse<T> {
public PagingLinks _links;
public List<T> _data;
}
Is there any way I can set up my RestAdapter so that it'll always map 'RESPONSE_DATA_NAME' to the '_data' member, no matter what the field name actually is?
Thanks ^_^
Using gson you can annotate your _data field with the #SerializedName. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field _data is represented as RESPONSE_DATA_NAME in JSON.
public class PagedResponse<T> {
public PagingLinks _links;
#SerializedName(value="RESPONSE_DATA_NAME")
public List<T> _data;
}
Further see doc
If you want to control the json field then you have to write custom de-serializer as like below
public class CustomDeserializer implements JsonDeserializer<PagedResponse> {
#Override
public PagedResponse deserialize(final JsonElement json,
final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
Gson gson = new Gson();
PagedResponse pagedResponse = new PagedResponse<>();
List list = new ArrayList<>();
pagedResponse = gson.fromJson(json, PagedResponse.class);
Type listType = new TypeToken<List>() {}.getType();
Set<Entry<String, JsonElement>> enteries = json.getAsJsonObject().entrySet();
for (Entry<String, JsonElement> entry : enteries) {
JsonElement jsonElement = (JsonElement) entry.getValue();
if (jsonElement.isJsonArray()) {
list.add(gson.fromJson(jsonElement, listType));
}
}
pagedResponse.set_data(list);
return pagedResponse;
}
}
finally parse it
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(PagedResponse.class, new CustomDeserializer());
Gson gson = gsonBuilder.create();
gson.fromJson(Your_JSON_STRING_HERE, PagedResponse.class);
So I finally found a solution to the problem...
I created a costume de-serializer, which adds the data field to the existing JsonObject, and copies the content of the RESPONSE_DATA_NAME (which is a JsonArray).
Then I serialize it normaly with GSON simple conversion (gson.fromJson()).
It's a bit stupid but it works =P
The de-serializer's class:
public class PagedResponseDeserializer implements JsonDeserializer<PagedResponse> {
#Override
public PagedResponse deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
JsonElement value = null;
JsonObject jsonObject = json.getAsJsonObject();
Iterable<Entry<String,JsonElement>> entries = jsonObject.entrySet();
for (Entry<String, JsonElement> entry : entries) {
value = entry.getValue();
if (value.isJsonArray()) break;
}
jsonObject.add("data", value);
return gson.fromJson(jsonObject, typeOfT);
}
}