I am creating a simple set of POJOs to hold incoming data for ISBN https://openlibrary.org/api/books
The REST API URL would look something like this:
https://openlibrary.org/api/books?bibkeys=ISBN:1234567890&format=json&jscmd=details
The responses of the REST call looks something like this (of course it's more complex than below example):
{
"ISBN:1234567890": {
"info_url": "https://openlibrary.org/books/OL23345657M/some_book_name",
"bib_key": "ISBN:1234567890"
}
}
The ISBN number changes based on the REST call made.
While trying to create a set of POJOs for the JSON structure, the POJO creator I used http://www.jsonschema2pojo.org/
creates root class like ISBN1234567890.java. Which of course may not be correct.
Should I pre-process the JSON string to replace the string
{
"ISBN:1234567890": {
with
{
"BookInfo": {
first so that my root class remains BookInfo.java?
I am guessing there could be better solution approach to this problem rather than doing String manipulation.
You could just implement a throw-away wrapper class, something like:
public class Wrapper {
public Map<String, BookInfo> info;
}
and peel out information you want from info property.
Related
I want to create two different JSON documents and each contains 5 fields. I have a POJO class with 10 attributes. I want to form json1 with 5 attributes and json2 with 5 attributes using that POJO class. Is there any way to construct these objects?
Consider writing two separate wrapper classes which each expose the fields you want for the two cases, and pass the pojo as a constructor arg.
So, one of them exposes one set of properties and might look like this:
public class JsonObject1 {
private MyPojo myPojo;
public JsonObject1(MyPojo myPojo) {
this.myPojo = myPojo;
}
public void getProperty1() {
return myPojo.getProperty1();
}
......
}
and the other is similar, but exposes the other subset of properties.
Alternatively, you could add two methods (possibly to your POJO, or possibly to a service class that is exposing the POJO) that each returns a Map (eg a HashMap) where you've copied across the specific properties you want for each view, and then convert those Maps to JSON. This is less "model-driven", but might be less work overall. Thanks to #fvu for this observation!
public Map<String, Object> getPojoAsMap1() {
Map<String, Object> m = new HashMap<>();
m.put("property1", pojo.getProperty1());
....
return m;
}
It's also possible that the two different JSON representations are trying to tell you that your POJO should be split up into two POJOs - sometimes things like this are hints about how your code could be improved. But it depends on the circumstances, and it might not apply in this case.
How to change from "setLineTokenizer(new DelimitedLineTokenizer()...)" to "JsonLineMapper" in the first code below? Basicaly, it is working with csv but I want to change it to read a simple json file. I found some threads here asking about complex json but this is not my case. Firstly I thought that I should use a very diferent approach from csv way, but after I read SBiAch05sample.pdf (see the link and snippet at the bottom), I understood that FlatFileItemReader can be used to read json format.
In almost similiar question, I can guess that I am not in the wrong direction. Please, I am trying to find the simplest but elegant and recommended way for fixing this snippet code. So, the wrapper below, unless I am really obligated to work this way, seems to go further. Additionally, the wrapper seems to me more Java 6 style than my tentative which takes advantage of anonimous method from Java 7 (as far as I can judge from studies). Please, any advise is higly appreciated.
//My Code
#Bean
#StepScope
public FlatFileItemReader<Message> reader() {
log.info("ItemReader >>");
FlatFileItemReader<Message> reader = new FlatFileItemReader<Message>();
reader.setResource(new ClassPathResource("test_json.js"));
reader.setLineMapper(new DefaultLineMapper<Message>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[] { "field1", "field2"...
//Sample using a wrapper
http://www.manning.com/templier/SBiAch05sample.pdf
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.JsonLineMapper;
import com.manning.sbia.ch05.Product;
public class WrappedJsonLineMapper implements LineMapper<Product> {
private JsonLineMapper delegate;
public Product mapLine(String line, int lineNumber) throws Exception {
Map<String,Object> productAsMap
= delegate.mapLine(line, lineNumber);
Product product = new Product();
product.setId((String)productAsMap.get("id"));
product.setName((String)productAsMap.get("name"));
product.setDescription((String)productAsMap.get("description"));
product.setPrice(new Float((Double)productAsMap.get("price")));
return product;
}
public void setDelegate(JsonLineMapper delegate) {
this.delegate = delegate;
}
}
Really you have two options for parsing JSON within a Spring Batch job:
Don't create a LineMapper, create a LineTokenizer. Spring Batch's DefaultLineMapper breaks up the parsing of a record into two phases, parsing the record and mapping the result to an object. The fact that the incoming data is JSON vs a CSV only impacts the parsing piece (which is handled by the LineTokenizer). That being said, you'd have to write your own LineTokenizer to parse the JSON into a FieldSet.
Use the provided JsonLineMapper. Spring Batch provides a LineMapper implementation that uses Jackson to deserialize JSON objects into java objects.
In either case, you can't map a LineMapper to a LineTokenizer as they accomplish two different things.
I'm using UUID as generator for my domain classes. When I render domain objects as JSON, the id looks like the following:
"id":{"class":"java.util.UUID","leastSignificantBits":-7570618374586820490,"mostSignificantBits":126481566314875615}
Instead, I want it to look as simple as,
"id":"01c15a50-7ed5-4adf-96ef-c2b2fcb51876"
which is how it looks like when I render the domain objects to XML. How do I make it work this way?
It may help if you post your domain class, but my assumption is it's something like the following:
class MyDomain {
UUID id = UUID.randomUUID()
}
Unless you explicitly need to keep it as a UUID, I imagine this will render the JSON as you're expecting:
class MyDomain {
String id = UUID.randomUUID().toString()
}
Because I required the UUID class so that ORM would work between Grails and Oracle, I couldn't just make the id field a String.
So instead, I used a very basic JsonRenderer to override the default (troublsome) JSON rendering of a UUID object.
#CompileStatic
class UuidJsonRenderer extends DefaultJsonRenderer<java.util.UUID> {
UuidJsonRenderer() {
super(java.util.UUID)
}
void renderJson(java.util.UUID object, RenderContext context) {
super.renderJson(object.toString(), context)
}
}
And plugged it in as a bean the following in resources.groovy:
beans = {
// Custom Renderers
uuidJsonRenderer(UuidJsonRenderer)
}
(I think you could probably annotate the renderer and have an annotation processor do this part automatically, if that's your pleasure.)
I need to add new property to an object, when serializing to JSON. The value for the property is calculated on runtime and does not exist in the object. Also the same object can be used for creation of different JSON with different set ot fields (kind of having a base class with subclasses, but I don't want to create ones just for JSON generation).
What is the best way of doing that, which doesn't involve creation of custom serializer class, which will take care of serializing of whole set of object's fields? Or may be it is possible to inherit some "basic" serializer, and simply take it's output and add new field to it somehow?
I learned about mixins, and looks like it is possible to rename/hide some fields, however it seems not be possible to add an extra one.
Can you not just add a method in value class? Note that it does not have to be either public, or use getter naming convention; you could do something like:
public class MyStuff {
// ... the usual fields, getters and/or setters
#JsonProperty("sum") // or whatever name you need in JSON
private int calculateSumForJSON() {
return 42; // calculate somehow
}
}
Otherwise you could convert POJO into JSON Tree value:
JsonNode tree = mapper.valueToTree(value);
and then modify it by adding properties etc.
2021 calling...
Simplest way I found to do this is #JsonUnwrapped:
public class Envelope<T> {
#JsonUnwrapped // content's fields are promoted alongside the envelope's
public T content;
// Transmission specific fields
public String url;
public long timestamp;
}
This works (bi-directionally) so long as Envelope's fieldnames do not clash with those of content. Also has a nice feature of keeping the transmission properties at the end of the serialised JSON.
One option is to add a field for this property and set it on the object before writing to JSON. A second option, if the property can be computed from other object properties you could just add a getter for it, for example:
public String getFullName() {
return getFirstName() + " " + getLastName();
}
And even though there's no matching field Jackson will automatically call this getter while writing the JSON and it will appear as fullName in the JSON output. If that won't work a third option is to convert the object to a map and then manipulate it however you need:
ObjectMapper mapper //.....
MyObject o //.....
long specialValue //.....
Map<String, Object> map = mapper.convertValue(o, new TypeReference<Map<String, Object>>() { });
map.put("specialValue", specialValue);
You're question didn't mention unmarshalling but if you need to do that as well then the first option would work fine but the second two would need some tweaking.
And as for writing different fields of the same object it sounds like a job for #JsonView
I have successfully set up a quick test of creating a "REST-like" service that returns an object serialized to JSON, and that was quite easy and quick (based on this article).
But while returning JSON-ified objects was easy as peach, I have yet to see any examples dealing with input parameters that are not primitives. How can I pass in a complex object as an argument? I am using Apache CXF, but examples using other frameworks like Jackson are welcome too :)
Client side would probably be something like building a javascript object, pass it into JSON.stringify(complexObj), and pass that string as one of the parameters.
The service would probably look something like this
#Service("myService")
class RestService {
#GET
#Produces("application/json")
#Path("/fooBar")
public Result fooBar(#QueryParam("foo") double foo, #QueryParam("bar") double bar,
#QueryParam("object") MyComplex object) throws WebServiceException {
...
}
}
Sending serialized objects as parameters would probably quickly touch the 2KB URL-limit imposed by Internet Explorer. Would you recommend using POST in these cases, and would I need to change much in the function definitions?
After digging a bit I quickly found out there are basically two options:
Option 1
You pass a "wrapper object" containing all the other parameters to the service. You might need to annotate this wrapper class with JAXB annotations like #XmlRootElement in order for this to work with the Jettison based provider, but if you use Jackson in stead there is no need. Just set the content type to the right type and the right message body reader will be invoked.
This will only work for POST type services of course (AFAIK).
Example
This is just an example of turning the service mentioned in the original question into one using a wrapper object.
#Service("myService")
class RestService {
#POST
#Produces("application/json")
#Path("/fooBar")
public Result fooBar(
/**
* Using "" will inject all form params directly into a ParamsWrapper
* #see http://cxf.apache.org/docs/jax-rs-basics.html
*/
#FormParam("") FooBarParamsWrapper wrapper
) throws WebServiceException {
doSomething(wrapper.foo);
}
}
class ParamsWrapper {
double foo, bar;
MyComplexObject object;
}
Option 2
You can provide some special string format that you pack your objects into and then implement either a constructor taking a string, a static valueOf(String s) or a static fromString(String s) in the class that will take this string and create an object from it. Or quite similar, create a ParameterHandler that does exactly the same.
AFAIK, only the second version will allow you to call your services from a browser using JSONP (since JSONP is a trick restricted to GET). I chose this route to be able to pass arrays of complex objects in the URI.
As an example of how this works, take the following domain class and service
Example
#GET
#Path("myService")
public void myService(#QueryParam("a") MyClass [] myVals) {
//do something
}
class MyClass {
public int foo;
public int bar;
/** Deserializes an Object of class MyClass from its JSON representation */
public static MyClass fromString(String jsonRepresentation) {
ObjectMapper mapper = new ObjectMapper(); //Jackson's JSON marshaller
MyClass o= null;
try {
o = mapper.readValue(jsonRepresentation, MyClass.class );
} catch (IOException e) {
throw new WebApplicationException()
}
return o;
}
}
A URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} would in this case be deserialized into an array composed of two MyClass objects.
2019 comment:
Seeing that this answer still gets some hits in 2019, I feel I should comment. In hindsight, I would not recomment option 2, as going through these steps just to be able to be able to do GET calls adds complexity that's probably not worth it. If your service takes such complex input, you will probably not be able to utilize client side caching anyway, due to the number of permutations of your input. I'd just go for configuring proper Cross-Origin-Sharing (CORS) headers on the server and POST the input. Then focus on caching whatever you can on the server.
The accepted answer is missing #BeanParam. See
https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html
for further details. It allows you to define query params inside a wrapper object.
E.g.
public class TestPOJO {
#QueryParam("someQueryParam")
private boolean someQueryParam;
public boolean isSomeQueryParam() {
return someQueryParam;
}
public boolean setSomeQueryParam(boolean value) {
this.someQueryParam = value;
}
}
... // inside the Resource class
#GET
#Path("test")
public Response getTest(#BeanParam TestPOJO testPOJO) {
...
}
the best and simplest solution is to send your object as a json string and in server side implement a method which will decode that json and map to the specified object as per your need.. and yes it`s better to use POST.