Modeling JSON in Java - java

The few times I've worked with Java/Rest/JSon, JSON elements have always been built in camelCase format.
For example:
"someField": {
"someSonField1": "20191106",
"someSonField2": "20201119",
...
}
However, in a functional document they have passed me to build a Rest JSon client, they use this notation:
"some_field": {
"some_son_field_1": "20191106",
"some_son_field_2": "20201119",
...
}
Is it expressed somewhere that Java has to use the notation 1?.
It seems to me that if it is done this way, everything goes much more smoothly when modeling the objects:
#XmlRootElement(name = "someField")
#XmlType(propOrder = {"someSonField1", "someSonField2"})
public class someField {
private String someSonField1;
private String someSonField2;
//...
}
Thanks!

Q: Is it expressed somewhere that Java has to use the notation?
A: No: it's 100% "convention", not mandatory.
As it happens, the standard convention for both JSON (a creature of Javascript) and Java is camelcase. For example: Java Naming Conventions.
some_son_field_1 is an example of snake case. It's associated with classic "C" programs. It's also common (but NOT universal) with XML. It, too, is a "convention" - not a requirement.
I'm curious why you're choosing XML bindings for JSON data. Have you considered using Jackson?
Finally, this article might be of interest to you:
5 Basic REST API Design Guidelines

I see you're using javax.xml.bind package? Have you tried #XmlElement?
#XmlRootElement(name = "someField")
#XmlType(propOrder = {"some_son_field_1", "some_son_field_2"})
public class someField {
#XmlElement(name="some_son_field_1")
private String someSonField1;
#XmlElement(name="some_son_field_2")
private String someSonField2;
//...
}
Not sure, probably you should try putting them on getters, as your fields are private.

Or you could use unify-jdocs, a library which I created to read and write JSON documents without using any POJO objects. Rather than defining POJO objects, which we know can be difficult to manage in case of complex documents and changes to the JSON document, just don't use them at all. Directly read and write paths in the JSON document. For example, in your snippet, you could read and write the fields as:
Document d = new JDocument(s); // where s is a JSON string
String s1 = d.getString("$.some_field.some_son_field_1");
String s2 = d.getString("$.some_field.some_son_field_2");
You could use a similar way to write to these paths as so:
d.setString("$.some_field.some_son_field_1", "val1");
d.setString("$.some_field.some_son_field_2", "val2");
This library offers a whole lot of other features which can be used to manipulate JSON documents. Features like model documents which lock the structure of documents to a template, field level validations, comparisons, merging documents etc.
Obviously, you would consider it only if you wanted to work without POJO objects. Alternatively, you could use it to read and write a POJO object using your own method.
Check it out on https://github.com/americanexpress/unify-jdocs.

Related

Good practices in REST / Java

I'm currently trying to acquire skills in REST, and specifically in "good" Rest, hypermedia and all the good practices that comes with it.
In order to do so, I was asked to develop a prototype REST server containing data of my choice and implementing everything I'll have to use in a real project coming after that.
So I made a server using Spring boot and Jackson for json handling.
My data architecture is close to this : I have a collection of LaunchVehicle (I like space =D) like Ariane V, Falcon 9, etc. I can retrieve the JSON object flawlessly
{ "name":"Ariane V","country":"Europe","firstFlight":null,"GTO_Payload":1.0,"LEO_Payload":2.3,"weight":136.0 }
The thing is, I'd like to add a "space agency" field which would be an object containing some Strings and Floats, inside my LaunchVehicle. However, when the client retrieve a LaunchVehicle, I don't want it to retrieve the full SpaceAgency object, just the name for exemple. From here, he would be able to follow the link to the space agency via an hypermedia link included in the response it would have received.
How can I do this ? Right now I'm only able to send to the client my full LaunchVehicle object with the SpaceAgency object and all his fields. Is there any annotations doing what I want ? Thanks ;)
public class LaunchVehicle {
private String name;
private String country;
private Date firstFlight;
private Map<String, Float> characteristics;
private SpaceAgency spaceAgency;
#JsonCreator
constructor...
#JsonProperty(required=false)
getters and setters...
}
Thanks a lot, don't hesitate if I'm not precise or understandable enough.
Try #JsonIgnoreProperties annotation at the class level. That should provide you the feature that you want.
Otherwise, you could always use some kind of DTO object to create your response model, and there just have the fields that are going to be used at the API layer.
I would rather prefer to use an appropiate DTO/ApiModel for your API layer than having a full domain object with JSON annotations in it.
If your SpaceAgency class only defines the properties that you need to deserialize, Jackson will only deserialize those. It will forget the unmapped properties.
Try jax-ws-rs!
It's a standart REST implementation in Java.
Oracle docs
Very good tutorial by Mkyong
You can use the Gson API for this
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(spaceAgency).getAsJsonObject();
String agencyName = obj.get("agencyName").getAsString();
I think you should reference the space agency as a hyperlink.
So the JSON will look like:
{ "name":"Ariane V",
"country":"Europe",
< other fields omitted >
"_links": {
"agency": { "href": "agencies/ESA" },
< other links omitted >
}
}
To achieve this you need to specify the link in your data transfer object. Don't make this a reference to an actual object of that type -- to do so would mean populating that object, even when the client doesn't ask for it.
How you achieve this depends on what technology you're using. In Jersey it's
public class LaunchVehicle {
...
#InjectLink(resource=AgencyResource.class)
URI agencyLink;
...
}
https://jersey.java.net/documentation/latest/declarative-linking.html
Linking like this is what "real" REST is all about. However note that plenty of real-world solutions claim to be doing REST without actually using hyperlinks. A more hacky solution would be to have a String agencyId field in your JSON, which could be put into a URL template to get agency details.

Check if JSON is valid in JAVA using Jackson

I have a JSON string which I am storing it in DB as a string. In front-end, I am rendering this JSON as object.
I am using:
JSON.parse(string);
Uncaught Syntax error: Unexpected Token
String :
{
"id": "295cd59f-4033-438c-9bf4-c571829f134e",
"from": "Shrisha S.<shrisha#s.com>",
"to": [
"Katie Porter <katie.porter#ss.com>"
],
"cc": [
"Jack d<jack.d#dd.com>, Keerthi<keerthi.s#dd.com>"
],
"bcc": [
]
}
Is there any way I can check If JSON is valid or not in JAVA?
One thing to be noted here is that, I don't have a schema defined for JSON which I can map to, i.e. JSON can hold anything.
I am currently trying out with JACKSON but for that I need a pre-defined schema which I don't have. Is there anyway this can be fixed?
You can read it as a JsonNode, no need to map it to a specific Class, its generic:
try{
ObjectMapper objectMapper = ...;
JsonNode jsonNode = objectMapper.readTree(yourJsonString);
} catch(JsonProcessingException e){........}
There are two different parts to the question. First is whether it is valid JSON, and second whether it contains specific set of information.
#pdem already answered first part (you can also read JSON as java.lang.Object to get the same effect).
But for second part, JSON Schema is not usually a good way, as it focuses on JSON aspects but not on more meaningful part of actual data, possible sub-typing and so on, which matter at Java level where all actual data processing occurs.
So usually you would define a POJO (or ideally just use one you use for actual data processing), bind to it (with ObjectMapper.readValue()), and then check whether data is not only technically valid wrt low-level data types, but also that it conforms to additional business constraints.
For latter part you can either write Java code, or use an annotation based framework such as Bean Validation API (JSR-303); see for example:
http://beanvalidation.org/
plus there are many #bean-validation tagged questions here as well related to usage. Some frameworks add explicit support for it; for example the best Java service framework, DropWizard does this. Others like Spring Boot have support as well.
JSON specification forbids it from using newline characters, make sure you are replacing newline characters see
Regex replace all newline characters with comma
make sure you do this before storing it in DB.
public boolean isValidJson(final String json) {
try {
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonNode = objectMapper.readTree(json);
return jsonNode instanceof ContainerNode;
} catch (JsonProcessingException jpe) {
return false;
}
}

Limiting Fields in JSON Response for REST API?

I am using Spring and Java and implementing REST Based services. I have a set of developers who develop for mobile,iPad and Web too. Consider I have a bean
Class User{
private String Name;
private Integer id;
private String photoURL;
private ArrayList<String> ProjectName;
private ArrayList<String> TechnologyList;
private ArrayList<String> InterestList;
//Getters and setters
}
While the Web Developers need the entire fields and mobile developers just require two fields from it whereas the iPad requires something in between mobile and web.
Since I am using jackson as a parser, is there a way where while requesting to the controller I can specify which all data I require and avoid the others. For example consider I do a GET request like
GET>http://somedomain.com/users?filter=name,id,photoUrl
Which returns me a JSON structure something like
{
"name":"My Name",
"id":32434,
"photoUrl":"/sss/photo.jpg"
}
Sameway if someone asks for some more fields, they could be filtered. Please let me know how this can be done so that my API remains generic and useable for all.
You can achieve what you want but some extra work is necessary. I can offer you two solutions.
1. Return a Map
Simply put every property that is requested into the map.
2. Use Jacksons Object Mapper directly
Jackson lets you set filters that specify which properties are serialized or ignored.
FilterProvider filter = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept(requestedProperties));
String json = objectMapper.writer(filter).writeValueAsString(value);
You can then return the JSON string directly instead of an object.
For both solutions you would ideally write a class that does the job. But if you do that you could as well write your own message converter. You could extend the MappingJackson2HttpMessageConverter, for instance, and overwrite the writeInternal method to suit your needs. That has the big advantage that you don't need to change your controllers.
The straightforward solution is to implement custom Jackson JSON serializer that will get field names that should be serialized from thread local storage and then serialize only fields which names are presented in that context. For other hand, in controller you can grab all allowed fields names from url and store them into thread local context. Hope this helps.

After I got XML data, how to parse it and transfer to JSON?

In Jersey RESTful frame work, I know I can get xml data in client as following:
private static final String BaseURI = "http://DOMAN.com";
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(BaseURI);
String xmlData = service.path("rest").path("todos").accept(
MediaType.APPLICATION_XML).get(String.class)
My question is how can I parse the xmlData then? I would like to get the needed data from xmlData, and transfer the needed data to JSON, what is the best way to implement this?
As a general rule, NEVER convert straight from XML to JSON (or vice versa) if you do not have to.
Rather, bind data from XML or JSON to POJOs, then do the other conversion. While it may seem non-intuitive this results in cleaner result and less problems, since conversions between POJOs and data formats have much more options, mature, well-designed libs; and POJOs are easier to configure (with annotations) and have more metadata to guide conversion process.
Direct conversions libs (like Jettison, see below) are plagued with various issues; often producing "franken-JSON", JSON that is technically correct but looks alien because of added constructs needed by conversion.
In case of Jersey, then, use JAXB for XML to/from POJOs, and Jackson for doing the same with JSON. These are libraries Jersey uses anyway; and direct usage is quite easy.
If you absolutely insist on direct conversion, you could try Jettison, but be prepared to hit a problem with Lists, arrays and Maps, if you need them (esp. single-element arrays -- arrays are problematic with XML, and auto-conversion often goes wrong).
If your service doesn't provide JSON as an option already (what happens if you change MediaType.APPLICATION_XML to MediaType.APPLICATION_JSON?), then I believe you have a few options, which I list in order of my preference.
Option 1: You have an XML schema for the the data
If you have an XML schema for the returned XML, you could use xjc to generate the JAXB annotated java classes and then leverage jackson to convert the instances to JSON data. I think this will get you going fast by leveraging this libraries over doing the parsing youself. Jackson is a robust library, used by glassfish for their Jersey(JAX-RS) implementation and I don't feel there is any risk in depending on this library.
Option 2: Use the json.org library, but I've had significant problem with this library having to do with its reflection-based methodology, etc. That said, it might work well for you...and you can test relatively easily and see if it does meet your requirements. If so...you're done! =)
Option 3: You don't have the XML schema and/or you want more control
as #Falcon pointed out, you can always use traditional XML parsing technologies to parse the XML into whatever you want. I'm partial to SAX parsing, but DOM could work depending on xml side
Regards,
Steve
Simplest and easiest way would be using org.json package : http://json.org/javadoc/org/json/XML.html
XML.toJSONObject(xmlData).toString()
Just this one line apart from necessary import statement will do it all.
Now that i have mentioned org.json library, lot of people may comment bad about it. Remember, I have said the simplest and easiest way, not the best or the most performant way ;-)
In case you are using maven, add this dependency :
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
Do you have any access to the "lower level interface" that generates the XML? If you do, the only change needed is to have the xml objects annotated with "#XmlRootElement". Then, you can just pass back the XMLobject as JSON without any further code.
Check Jsonix. If you have an XML schema, you can generate XML-JSON mappings and unmarshal/marshal XML in JavaScript. Very similar to JAXB (which Steve Siebert mentioned), but works on client.
// The PO variable provides Jsonix mappings for the purchase order test case
// Its definition will be shown in the next section
var PO = { };
// ... Declaration of Jsonix mappings for the purchase order schema ...
// First we construct a Jsonix context - a factory for unmarshaller (parser)
// and marshaller (serializer)
var context = new Jsonix.Context([ PO ]);
// Then we create an unmarshaller
var unmarshaller = context.createUnmarshaller();
// Unmarshal an object from the XML retrieved from the URL
unmarshaller.unmarshalURL('/org/hisrc/jsonix/samples/po/test/po-0.xml',
// This callback function will be provided with the result
// of the unmarshalling
function(result) {
// We just check that we get the values we expect
assertEquals('Alice Smith', result.value.shipTo.name);
assertEquals('Baby Monitor', result.value.item[1].productName);
});

parse google geocode with xstream

I'm using Java and XStream to parse a google geocode request over http. My idea is to have an Address class with all the geocode attr's (ie. lat/long, city, provice/state etc) but I'm having problems parsing the xml with xstream.
The google response is similar to this:
<?xml version="1.0" encoding="UTF-8" ?>
<kml xmlns="http://earth.google.com/kml/2.0"><Response>
<name>98 St. Patrick St, Toronto</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>98 St Patrick St, Toronto, ON, Canada</address>
<AddressDetails Accuracy="8" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"> <Country><CountryNameCode>CA</CountryNameCode><CountryName>Canada</CountryName><AdministrativeArea><AdministrativeAreaName>ON</AdministrativeAreaName><Locality><LocalityName>Toronto</LocalityName><Thoroughfare><ThoroughfareName>98 St Patrick St</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>M5T</PostalCodeNumber></PostalCode></Locality></AdministrativeArea></Country></AddressDetails>
<ExtendedData>
<LatLonBox north="43.6560378" south="43.6497426" east="-79.3864912" west="-79.3927864" />
</ExtendedData>
<Point><coordinates>-79.3896388,43.6528902,0</coordinates></Point>
</Placemark>
</Response></kml>
That doesn't show up very well, but the meat of the code is in the AddressDetails tag.
Anyway, I'm new to Java and XStream so the API terminology is a bit confusing for me. I just need to be able to write some mapper that maps all these tags (ie. CountryName) to an attribute within my Address object, (ie. address.country = blah) The address object will be pretty simple, mainly just strings for country name etc and floats for lat/long.
The docs and example just show straight mapping where each xml tag maps directly to the attribute of the same name of the object. In my case however, the tags are named different than the object attr's. A quick point in the right direction is all I'm looking for really.
I've used XStream in several projects. Unfortunately, your problem isn't really what XStream is designed to solve. You might be able to use its converter mechanism to achieve your immediate goal, but you'll run into limitations. In a nutshell, XStream isn't designed to do conversion of Tree Structure A into Tree Structure B -- it's purpose is to convert from a Java domain model into some reasonable XML. XStream is a great tool when you don't care much about the details of the XML produced. If you care more about the XML than the Java objects, look at XMLBeans -- the Java is ugly, but it's incredibly schema-compliant.
For your project, I'd run the Google XML schema through XML beans, generate some Java that will give you a more literate way of hand-coding a converter. You could use a raw DOM tree, but you'd have code like myAddress.setStreet(root.getFirstChild().getAttribute("addr1"))). With XML beans, you say things like myAddress.setStreet(googleResult.getAddress().getStreetName();
I'd ignore JAXB as it's attempt to separate interface from implementation adds needless complexity. Castor might be a good tool to consider as well, but I haven't used it in years.
In a nutshell, there aren't a lot of good Object-to-Object or XML-to-Object converters that handle structure conversion well. Of those I've seen that attempt declarative solutions, all of them seemed much more complicated (and no more maintainable) than using XStream/XmlBeans along with hand-coded structure conversions.
Would it be possible to define a separate class specifically for dealing with XStream's mapping? You could then simply populate your AddressDetails object by querying values out of this other object.
I've ended up just using xpath and populating my own address object manually. Seems to work fine.
Have you tried with json format? It should be the same but you'll need to set a com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver as the driver for XStream
You could use EclipseLink JAXB (MOXy) to do this:
package com.example;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="kml")
public class Address {
private String country;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:CountryName/text()")
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
and
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://earth.google.com/kml/2.0",
xmlns = {
#javax.xml.bind.annotation.XmlNs(
prefix = "ns", namespaceURI ="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example;
A full example is available here:
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html

Categories

Resources