Object constructor from json or xml string - java

I'm working on a service that spits json everywhere. However one of the providers I consume uses XML as serialization format so I want to be able to use the same interface for parsing (and spitting out) JSON with that XML.
Problem is, I don't know of a XML type, or object that would allow me to override my constructor easily.
Hoping to clarify my point, here's some code:
public class JsonData {
private Hashtable<String, Variant> map = new Hashtable<String, Variant>();
public JsonData() {
}
public JsonData(String jsonString) {
this.deserialize(jsonString);
}
Ideally I would like a third constructor to do something like:
public JsonData(XMLString jsonString) {
this.xmldeserialize(jsonString);
}
Note how both relevant constructors take a plain string as argument.
Any pointer?

You can use static methods to create object from json or xml strings:
public static JsonData fromJson(String json) {
JsonData data = new JsonData();
data.deserializeJson(json);
return data;
}
public static JsonData fromXml(String xml) {
JsonData data = new JsonData();
data.deserializeXml(xml);
return data;
}

Can't you just check if the input is json or xml (using regex) and call appropriate method to deserialize.
Like
public JsonData(String jsonString) {
if(isValidJson(jsonString){
this.deserialize(jsonString);
} else {
this.xmldeserialize(jsonString);
}
}

Related

Parse a JSON structure with symbol instead of a string for an attribute

I am working on an HTTP Rest query.
Wikipedia API returns a JSON. The problem is the JSON structure returned by Wikipedia.
https://en.wikipedia.org/w/api.php?action=parse&section=0&prop=text&format=json&page=pizza
This is the JSON obtained via a rest request to the Wikipedia API. To view the full JSON, you can click on the above link.
{
"parse":
{
"title":"Pizza",
"pageid":24768,
"text":{"*":"<div class=\...>"}
}
}
I would parse this using a custom deserializer which I haven't got a chance to test. Trying to parse with simple Gson like the following return null;
Result res = new Gson(str,Result.class);
I have created the classes like the following:
public class Result
{
private Parse parse;
}
public class Parse
{
private String title;
private int pageid;
private Text text;
}
public class Text{
private String *;// what should I call this attribute.
}
My plan is to add a custom deserializer like the following:
public class TextBaseDeserializer implements JsonDeserializer<Text> {
#Override
public RespondentBase deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return jsonDeserializationContext.deserialize(jsonElement.get("*"),Text.class);
}
}
I am using Gson to parse this, like the following:
Gson tmp = new GsonBuilder().registerTypeAdapter(Text.class, new TextBaseDeserializer());
// let's assume that str is the string obtained following Rest based request from Java.
Result res = tmp.parse(str,Result.class);
I have done all the above code to handle the symbolic JSON attribute.
My question is how to parse such a JSON. In the above example, the attribute is a *
As mentioned in a comment, you should annotated field with #SerializedName("*").
You can name the field whatever you want. We'll just name it star below, but maybe all is better, since * might be a wildchar? Doesn't really matter, just choose whatever name you like.
class Text
{
#SerializedName("*")
private String star;
}
Test
String str = "{\"parse\":{\"title\":\"Pizza\",\"pageid\":24768,\"text\":{\"*\":\"<div class=\\\"mw-parser-output\\\">...</div>\"}}}";
Gson tmp = new GsonBuilder().create();
Result res = tmp.fromJson(str, Result.class);
System.out.println(res.getParse().getText().getStar());
Output
<div class="mw-parser-output">...</div>

Does Gson ignore #JsonAdapter on JsonElement fields?

I have an object to serialize using Gson:
class User {
String firstname;
String lastname;
JsonElement opaqueData;
}
On the top object level, I want Gson to ignore null fields (say, if lastname is null, it should not be serialized).
But in field opaqueData , I want nulls to be serialized since it is supposed to a block of opaque data. So using new GsonBuilder().serializeNulls().create().toJson(user) would not work.
But #JsonAdapter does not work either
class User {
String firstname;
String lastname;
#JsonAdapter(NullAdapter.class)
JsonElement opaqueData;
}
class NullAdapter extends TypeAdapter<JsonElement> {
#Override
public void write(JsonWriter out, JsonElement value) throws IOException {
out.jsonValue(new GsonBuilder().serializeNulls().create().toJson(value));
}
#Override
public JsonElement read(JsonReader in) throws IOException {
// TODO Auto-generated method stub
return null;
}
}
It appears that the adapter is ignored by Gson.
Is this expected behavior? How can make this happen without create an adapter for the whole User class then manually serialize each field?
Gson does not bother to adapt its internal data type JsonElement. I do not know if there is any point in since JsonElement kind is the adapted result.
Having a glance at the source code of Gson you can see there two top level methods:
For any object
public String toJson(Object src) {
if (src == null) {
return toJson(JsonNull.INSTANCE);
}
return toJson(src, src.getClass());
}
I think it is obvious without copying the rest of the code that above method one has searching & setting of adapters (and querying adapter factories) in its execution path. But then there is also method like:
For JsonElement
public String toJson(JsonElement jsonElement) {
StringWriter writer = new StringWriter();
toJson(jsonElement, writer);
return writer.toString();
}
and for that execution path there is no searching nor setting any adapters as JsonElement is not meant to be adapted anymore.
So when Gson hits any JsonElement it just serializes ti as formatted string and you cannot affect on that wiht any adapter.
If you really need to serialize something like that maybe you could make a wrapper class for the JsonElement, something like:
public class JsonElementWrapper {
public JsonElement jsonElement;
}
and adjust your TypeAdapter to adapt that to `null``or actual value.

Spring Boot REST application with Jackson - how to handle arbitrary/unknown xml structures passed in?

I am working with a Spring Boot REST application. We are using jackson to handle deserialization of XML as well as JSON passed in the request body. An example of an expected request body looks like this:
<formInput><formNum>999999</formNum><documentData>Completely unknown data structure here!</documentData></formInput>
In the documentData element, we will have a structure that is completely arbitrary/unknown on the server side. We don't care about the structure, because we only want to pass the xml that is nested in documentData on to another service.
The POJO that we are trying to map the request body onto looks like this:
#JsonDeserialize(using=FormInputJsonDeserializer.class)
public class FormInput {
private String formNum
private String documentData
public String getFormNum() {
return formNum
}
public void setFormNum(String formNum) {
this.formNum = formNum
}
public String getDocumentData() {
return documentData;
}
public void setDocumentData(String documentData) {
this.documentData = documentData;
}
}
The custom JsonDeserializer that we are trying to write:
public class FormInputJsonDeserializer extends JsonDeserializer<FormInput> {
#Override
public FormInput deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
FormInput formInput = new FormInput();
String fieldName
JsonToken currentToken
while (parser.nextToken() != null) {
currentToken = parser.getCurrentToken()
if (currentToken.equals(JsonToken.END_OBJECT)) {
continue
}
fieldName = parser.getCurrentName()
// formNum handling not written yet
if ("documentData".equalsIgnoreCase(fieldName)) {
if (parser.getCurrentToken().equals(JsonToken.START_OBJECT)) {
// we are at the start of documentData, and we need to capture the
// entire documentData node as a String since we don't know
// its structure
JsonFactory jfactory = new JsonFactory()
StringWriter jsonStringWriter = new StringWriter()
JsonGenerator jGen = jfactory.createGenerator(jsonStringWriter)
jGen.copyCurrentStructure(parser) // points to END_OBJECT after copy
jGen.close()
String documentDataJsonStr = jsonStringWriter.getBuffer().toString()
println("documentDataJsonStr: " + documentDataJsonStr)
}
}
}
// rest of code omitted
}
}
As I say, if the request body is xml, ideally I'd like to just keep it formatted as xml and assign that to the documentData String property. However I came up with the above custom deserialization code by following some other examples on StackOverflow. This parsing code ends up converting documentData to a JSON formatted String. Since I didn't know how to pass through the raw XML and get it mapped to the String property, I thought I could just convert the JSON formatted String back to a XML formatted String. A problem arises when we pass in a XML structure like this:
<formInput><formNum>9322</formNum><documentData><repeatLevel><subForm1><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR></subForm1></repeatLevel><repeatLevel><subForm1><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR></subForm1></repeatLevel></documentData></formInput>
After documentData is parsed in the deserialize method, the println statement shows the parsed JSON String as:
{"repeatLevel":{"subForm1":{"GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31"}},"repeatLevel":{"subForm1":{"GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37"}}}
This is actually not strictly valid JSON, due to the duplicate keys. I would have hoped that these would have been converted to JSON arrays, but that is not the case. So, I am unable to turn around and use something like the JSON.org libraries (JsonObject and XML) to convert the JSON String back to XML format (get an exception with a "duplicate key" error).
Does anybody have any suggestions or strategies for handling our situation?
You could try to use a JSONObject, add the #JsonIgnoreProperties("documentData") tag and extract documentData seperately from the raw data using substring()

JSON string serialization ( to camel case ) and deserialization (from snake case )using Jackson

I am trying to deserialize a json string to POJO and then serialize it back to json string using Jackson, but in this process I want resultant json string to have changed key values.
e.g. input json string:
{"some_key":"value"}
here is what my POJO looks like
public class Sample {
#JsonProperty("some_key")
private String someKey;
public String getSomeKey(){
return someKey ;
};
}
When I serialize it again I want json string to be something like this
{"someKey":"value"} .
Is there any way I can achieve this?
I was able to do deserialization by renaming setter functions according to what is the input json string .
class Test{
private String someKey;
// for deserializing from field "some_key"
public void setSome_key( String someKey) {
this.someKey = someKey;
}
public String getSomeKey(){
return someKey;
}
}
You should be able to pull this off by defining a creator for deserialization and then let Jackson do its default behavior for serialization.
public class Sample {
private final String someKey;
#JsonCreator
public Sample(#JsonProperty("some_key") String someKey) {
this.someKey = someKey;
}
// Should serialize as "someKey" by default
public String getSomeKey(){
return someKey;
}
}
You may need to disable MapperFeature.AUTO_DETECT_CREATORS on your ObjectMapper for this to work.

Parsing JSON String as simple as possible with GSON

Using GSON, how can i return a single key from a Multidimensional Json String?
Here is the Multidimensional Json String:
{"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
I want to return the "session_token" key value.
I'm trying this:
class app {
static class Response {
String session_token;
}
public void getSessionToken() {
String x = {"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
Response r = new Gson().fromJson(x, Response.class);
System.out.println(r.session_token);
}
}
But with this, my r.session_token returns null.
You would need to use Gson's JsonParser class directly and extract the data from the parse tree:
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(myJsonString);
JsonObject jsonObject = element.getAsJsonObject();
String lastName = jsonObject.get("lastname").getAsString();
System.out.println(lastName);
That said, it's debatable whether this would save you any real time over:
(edited from comments below):
class App {
static class Response {
String lastname;
}
public static void main(String[] args) {
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
Response r = new Gson().fromJson(myJsonString, Response.class);
System.out.println(r.lastname);
}
}
Gson will silently ignore the fact that there's more data in the JSON than you're interested in, and later on you might be interested in it, in which case it's trivial to add fields to your Response class.
Edit due to question changing:
You have a JSON object. It contains a field data whose value is an object. Inside that object you have a field session_token that you're interested in.
Either you have to navigate to that field through the parse tree, or you have to create Java classes that all will map to. The Java classes would resemble (at the bare minimum):
class Response {
Data data;
}
class Data {
String session_token;
}

Categories