Handling different JSON Request Jackson - java

I am working in a project where I need to send a request to remote service with 2 different formats
Format 1:
{
"templateId": "template1",
"configurationData": {
"inboundHeaders": [
{
"key": "header1",
"value": "value1"
}, {
"key": "header2",
"value": "value2"
}, {
"key": "header3",
"value": "value3"
}
],
"outboundHeaders": [
{
"key": "header4",
"value": "value4"
}, {
"key": "header5",
"value": "value5"
}, {
"key": "header6",
"value": "value6"
}
]
}
}
Format 2
{
"templateId": "template1",
"configurationData": {
"inboundHeaders": "head1",
"outboundHeaders" : "head2,head3"
}
}
Now I have created one class
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationData {
#JsonProperty("inboundHeaders")
private List<Header> inboundHeaders = null;
#JsonIgnore
#JsonProperty("outboundHeaders")
private List<Header> outboundHeaders = null;
#JsonProperty("inboundHeaders")
private String inboundHeader = null;
#JsonProperty("outboundHeaders")
private String outboundHeader = null;
}
Getters and Setters would go here....
But when I am executing this program. Obviously, I am getting following exception like
com.fasterxml.jackson.databind.JsonMappingException: Multiple fields representing property
How to handle these two different version (java.util.List vs java.lang.String) of requests in one Json POJO?

I think you have two options.
Create two classes and two methods to call remote service like:
Lists:
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationDataLists {
#JsonProperty("inboundHeaders")
private List<Header> inboundHeaders = null;
#JsonIgnore
#JsonProperty("outboundHeaders")
private List<Header> outboundHeaders = null;
}
Strings:
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationDataString {
#JsonProperty("inboundHeaders")
private String inboundHeader = null;
#JsonProperty("outboundHeaders")
private String outboundHeader = null;
}
Use Map
I will prefer option 1.

The answer from Francisco PĂ©rez is absolutely correct but you later clarified your question. The possibilities to limit new classes are - well - limited. You either need to create a class representinng each different DTO or make some sort of manual serializing.
One thing you can do is that you create an interface for different types of configuration data DTOs, so just:
interface IConfigurationData {}
then you have this template create or change it so that configurationData is of type tha interface:
#Getter #Setter
public class Template {
private String templateId;
private IConfigurationData configurationData;
}
Then using the DTO classes in above mentioned answer let them implement this interface, like:
public class ConfigurationDataLists implements IConfigurationData {...}
and
public class ConfigurationDataString implements IConfigurationData {...}
Then you will be able to do two different queries like this:
Template template1 = new Template();
template1.setTemplateId("1");
template1.setConfigurationData(new ConfigurationDataLists());
Template template2 = new Template();
template2.setTemplateId("2");
template2.setConfigurationData(new ConfigurationDataString());

You cannot use the same name to different properties like you did. E.g. - inboundHeaders.
You have to change one of the propertyname. In simple words you have to keep the
#JsonProperty
unique.

Related

Map with object and list of objects to Json

I have got two main model classes: Customer and Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
I want to build json file with Map<Customer, List<Product>>
When I write to json file data with my method which works correct - I am sure about this - the json file shows this syntax
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
Then when i want to read this file, the error Exception in thread "main" java.util.NoSuchElementException: No value present occurs so I think that the Customer syntax from json file is not recognized.
So I tried to write data to json file on my own with this syntax below, but it does not work
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json converter method:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
#Tom is right on the issues you've faced with. I'll explain why and suggest one more solution.
Your first JSON is technically a valid JSON but it cannot be deserialized, because the map keys are results of the Customer.toString() method Gson uses by default. This is why it looks weird, acts like a debug string, and can't be deserialized back: there it is almost always no way to restore an object from the toString() result (toString is designed mostly for debugging/logging purposes providing basic information regarding the state of a particular object that does not need to expose its all internals at all).
Your second JSON is invalid JSON. Period.
Tom's suggestion of making the list of products a part of the customer class is totally fine. Having it implemented like that lets you to serialize everything as a list like this:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
Hint: separating domain objects (Customer and Product) and representation objects for data transfer (CustomerDto and ProductDto) is usually a fine idea too since it allows to create representation for any concrete representation implementation (one for various JSON implementation libraries, two for other-format-oriented tools, third for persistence, four for UI views, etc), so it might be implemented like converting Map<Customer, List<Product>> to List<CustomerDto> and back (possibly by using mapper-generators like MapStruct).
If for whatever reason it is not possible to reorganize your domain classes or create Gson-friendly DTO-mappings, or you're fine to keep it as simple as possible and you're fine with having not that trivial JSON structure (as long as you understand implications of the format in this solution: evolution, distribution, etc), then you can enable special Gson mode to support this kind of maps. It generates valid JSONs that can be serialized and deserialized back, but the way it is implemented looks a bit of anti-pattern to me because of losing semantics due to using arrays as the data container.
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Customer {
final String name;
}
#AllArgsConstructor
#EqualsAndHashCode
#ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
#Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
Note that it generates JSON like this (index 0 means the key, index 1 means the value):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]

Is it Possible to send a Java Object to C# App using Kafka

Is it possible to send send a Java Object (lets say user) to a topic that is consumed and serialised to a User Object in C#?
Lets say I have the following avro schema built from a a Java PoJo (Fields are Name and Age)
{
"namespace": "io.confluent.developer",
"type": "record",
"name": "User",
"fields": [
{
"name": "name",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "age",
"type": [
"null",
"int"
],
"default": null
}
]
}
Which generates a User.class
Then sending it like so:
Service
#CommonsLog(topic = "Producer Logger")
#RequiredArgsConstructor
public class Producer {
#Value("${topic.name}")
private String TOPIC;
private final KafkaTemplate<String, User> kafkaTemplate;
void sendMessage(User user) {
this.kafkaTemplate.send(this.TOPIC, user.getName(), user);
log.info(String.format("Produced user -> %s", user));
}
}
I also have a Schema registry BUT I do not know how to consume the message in C# and deserialise it to a User class with the same fields:
public class Users
{
public int id = 0;
public string name = string.Empty;
public Users()
{
// Constructor Statements
}
public void GetUserDetails(int uid, string uname)
{
id = uid;
uname = name;
Console.WriteLine("Id: {0}, Name: {1}", id, name);
}
public int Designation { get; set; }
public string Location { get; set; }
}
Thanks for the help.
Yes it's possible. You can use official .NET kafka client to consume messages.
First thing you have to do is generate C# class based on the same schema you used. You can do that by:
Installing avrogen tool:
dotnet tool install --global Apache.Avro.Tools
Generate the class: avrogen -s user_schema.avsc .
Then you will get User.cs with the class implementation. All you need to do is configure .NET Kafka client and consume the messages:
var schemaRegistryConfig = new SchemaRegistryConfig
{
Url = "schemaRegistryUrl"
};
var consumerConfig = new ConsumerConfig
{
BootstrapServers = "bootstrapServers",
GroupId = "group"
};
using var schemaRegistry = new CachedSchemaRegistryClient(schemaRegistryConfig);
using var consumer = new ConsumerBuilder<string, User>(consumerConfig)
.SetValueDeserializer(new AvroDeserializer<User>(schemaRegistry).AsSyncOverAsync())
.Build();
consumer.Subscribe(topicName);
var consumeResult = consumer.Consume(cts.Token);
You can have a look at this example for more info.
Notice, that you can't use the User class you provided in your question, because there are some requirements for the class structure. So you should use the one generated with the tool from your Avro schema.

How to manipulate nested Json Data to de-Nest in java

I have been looking solution for this problem but could not find one so asking this question.
I have some data which looks like this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation": {
"firstName": "jio",
"lastName": "g",
"mobileNumber": "1234567890",
},
"accountBalance": 0,
}
]
},
I want to write a java code to change the data structure to this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation_firstName":"jio",
"personalInformation_lastNAme":"g",
"personalInformation_mobileNumber":"1234567890",
"accountBalance": 0,
}
]
},
I am getting data from db as:
#Override
public List<User> getAllUsers() {
logger.debug("entering all users method");
List<User> allUsers=mongoOperations.findAll(User.class);
for (User user : allUsers) {
PersonalInformation info=user.getPersonalInformation());
//manipulation code here
user.setPersonalInformation(info);
}
return allUsers;
}
So I want to write a logic so that i can convert the data in desired format and send it a return type. I know how to do same thing using J query but I want to do it in backend so any code for the above or any link will help.
I have fond one solution which is very simple.So, basically when we create object for nested data we create it like this in JAVA.
public MyClass{
public String name;
public String contact;
public PersonalInformation personalinformation;
//setters and getter here
}
this will give me data as
"MyClass":{
"name": "abc",
"contact": "12345",
"personalInformation":{
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
}
so to remove this nested data we need to use #JsonUnwrapped which removes all the nested object and add it to our main object.
public MyClass{
public String name;
public String contact;
#JsonUnwrapped
public PersonalInformation personalinformation;
//setters and getter here
}
which will change the data structure as:
"MyClass":{
"name": "abc",
"contact": "12345",
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
for more reference you can check this link http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Hope this helps.
There are multiple possible solutions. As Prabhav has mentioned the most intuitive one would be to create a new class and from there a object which can be transformed with a library to a JSON.
Variant one:
The new class would look like your data structure you want and access would be:
PersonalInformationJson pf = new PersonalInformationJson();
pf.setFirstName = info.getPersonalInformation_firstName
//... setting the rest of the object
//using jackson
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
String jsonString = mapper.writeValueAsString(u);
}
The other easier version to create a string, either per hand or use a lib:
// using org.json.JSONObject
String jsonString = new JSONObject().put("personalInformation_firstName", info.value())
.put("personalInformation_lastNAme", info.value());

How construct list of objects from json string coming from api response?

Similar question might be asked before on here, but I had no luck and I was wondering how to extract specific objects like user in from below json string and then construct an ArrayList. However, there is one twist, one of the property directly under Users is a random number, which can be anything!!!
Here is how my json string looks like:
<code>{
"_links": {
},
"count": {
},
"users": {
"123321": { //*Is a random number which can be any number
"_links": {
},
"user": {
"id": "123321",
"name": "...",
"age": "...",
"address": ""
..
}
},
"456654": {
"_links": {
},
"user": {
"id": "456654",
"name": "...",
"age": "...",
"address": ""
...
}
}
...
},
"page": {
}
}
</code>
The java object I would like to transform it to is:
#JsonIgnoreProperties(ignoreUnknown = true) // Ignore any properties not bound here
public class User {
private String id;
private String name;
//setter:getter
}
Note: The transformation should only consider those two fields (id,name), and ignore the rest of the fields from the json response user:{} object.
Ideally, I would like to end up with a list like this:
List<User> users = resulted json transformation should return a list of users!!
Any idea how can I do this please ideally with Jackson JSON Parser/ or maybe GSON?
Since the user keys are random, you obviously can't map them to a named Java field. Instead, you can parse the top-level object as a map and the manually pull out the user objects.
public class UserWrapper {
private User user;
public User getUser() { return user; }
}
public class Root {
private Map<String, UserWrapper> users;
public List<User> getUsers() {
List<User> usersList = new ArrayList();
for (String key : map.keySet()) {
UserWrapper wrapper = map.get(key);
usersList.add(wrapper.getUser());
}
return userList;
}
}
Root root = parseJson();
List<User> users = root.getUsers()
Hope that helps!
jolt transformer is your friend. Use shift with wildcard * to capture arbitrary node value and then standard mappers (Jackson /gson) .

How do I deserialize this JSON using Jackson?

I have json that looks like this:
{
"summary":{
"somefield1":"somevalue1",
"Twilio":{
"field1":"value1",
"field2":"value2"
},
"Tropo":{
"field1":"value1",
"field2":"value2"
},
...
}
}
I would like to deserialize it into a java class that looks like this:
public class Summary {
private String someField1;
private List<Vendor> vendors;
}
public class Vendor {
private String name;
private String field1;
private String field2;
}
So the Twilio and Tropo need to become Vendor objects in a list where Vendor.name == "Twilio" or "Tropo".
I'm sure jackson has the tools I need to work with this structure but I've been striking out with web searches.
You can do it with combination of #JsonRootName and #JsonAnySetter annotations. Your Summary class should look like this:
#JsonRootName("summary")
class Summary {
#JsonProperty("somefield1")
private String someField1;
private List<Vendor> vendors = new ArrayList<Vendor>();
#JsonAnySetter
public void setDynamicProperty(String vendorName, Map<String, String> properties) {
Vendor vendor = new Vendor();
vendor.setName(vendorName);
vendor.setField1(properties.get("field1"));
vendor.setField2(properties.get("field2"));
vendors.add(vendor);
}
//getters,setters,toString methods
}
Example usage:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
System.out.println(mapper.readValue(json, Summary.class));
Above source code shows below string for your JSON:
Summary [someField1=somevalue1, vendors=[Vendor [name=Twilio, field1=value1, field2=value2], Vendor [name=Tropo, field1=value1, field2=value2]]]
If you want to use your objects:
public class Summary {
private String someField1;
private List<Vendor> vendors;
}
public class Vendor {
private String name;
private String field1;
private String field2;
}
you have to modify your json. Actually a structure like the one you defined will be converted to something like:
{
"summary": {
"somefield1": "somevalue1",
"vendors": [
{
"name": "Twilio",
"field1": "value1",
"field2": "value2"
},
{
"name": "Tropo",
"field1": "value1",
"field2": "value2"
}
]
}
}
a list is defined between the square brackets [], and in your case it's a list of objects {}.
I would change your json if you can, because the structure you post will be a mess to work with. The one I pointed out, that matches your java objects, is more clear.
The JSON structure you've got would match this java structure where the key of vendors is the vendor name.
public class Summary {
private String someField1;
private Map<String,Vendor> vendors;
}
public class Vendor {
private String field1;
private String field2;
}
The classes you've specified would support this JSON:
{
"somefield1":"somevalue1",
"vendors":[{
"name":"Twilio"
"field1":"value1",
"field2":"value2"
},
{
"name":"Tropo"
"field1":"value1",
"field2":"value2"
},
...]
}
I don't think you can achieve what you want with jackson as the name is outside the "Vendor" object.

Categories

Resources