How to reduce all the fields of object in java - java

I have a Pojo class with all numeric fields
public class Pojo {
private long field1;
private long field2;
private long field3;
private long field4;
private long field5;
private double field6;
private double field7;
private double field8;
private double field9;
}
And there is a list of Pojo, I want map this list to one Pojo object which will contain in its field the of pojos foe, list. I mean someething like this :
public static void main(String[] args) {
List<Pojo> pojoList = getPogoList();
Pojo pojoInSum = reduceAllFields(pojoList);
}
What is the simplest way to reduce all fields of pojos from list without reflection?

You could use the Stream#reduce method:
public void reducePojoList() {
PojoReduce pojoReduce = new PojoReduce();
List<Pojo> pojoList = Arrays.asList(
new Pojo(3L, 4.0),
new Pojo(6L, 1.1));
Optional<Pojo> reducedPojo = pojoList.stream().reduce(this::combine);
reducedPojo.ifPresent(System.out::println);
}
private Pojo combine(Pojo pojo1, Pojo pojo2) {
return new Pojo(
pojo1.getLongField() + pojo2.getLongField(),
pojo1.getDoubleField() + pojo2.getDoubleField()
);
}
You would have to update the combine method for every field you add though. You'd also be creating a lot of new objects.

The simplest way is to write a method in that pojo. Because if you are modeling a thing in a class you should expose behavior and not data.
But I doubt that is what you are looking for so you might want to look at reflection.
Basically you retrieve all the fields of a class, get the values for the instance and then sum them in a loop or stream.

Related

How to combine multiple thirdparty POJO into single POJO

We have multiple third party pojo which we wan to combine into single pojo and use that single pojo to map to a JSON using jackson.
third party pojo -
public class ThirdPartyPojo1 {
private String random1
//public setters and getters
}
public class ThirdPartyPojo2 {
private String random2
//public setters and getters
}
we wan to combine these to form a single pojo like -
public class ourPojo {
private String random1;
private String random2;
//public setters and getters
}
we will use jackon to serialize this into a JSON string. How can we achieve this?
This is my way to solve your question. I am not sure if it has better solution so you may take this as a reference. I use ObjectReader readerForUpdating() to merge multiple json sources but this is limited to shallow copy. You may need another way to do with more complicated object. Here is my code:
package jackson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
public class Test {
static ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
// Create instance and convert it to json
ThirdPartyPojo1 obj1 = new ThirdPartyPojo1();
obj1.setRandom1("value 1");
String json1 = toJson(obj1);
// Create instance and convert it to json
ThirdPartyPojo2 obj2 = new ThirdPartyPojo2();
obj2.setRandom2("value 2");
String json2 = toJson(obj2);
// Suppose the field names of ThirdPartyPojo are corresponding to ourPojo
// Firstly, use ObjectMapper readValue() to get ThirdPartyPojo1 field i.e. random1
ourPojo obj3 = mapper.readValue(json1, ourPojo.class);
// Secondly, use ObjectReader to update ourPojo object
// Notes that it makes shallow copy only
ObjectReader updater = mapper.readerForUpdating(obj3);
// Update ourPojo from ThirdPartyPojo2 field i.e. random2
obj3 = updater.readValue(json2);
// The result displays a merging json from your single POJO
System.out.println(toJson(obj3));
}
static String toJson(Object obj) throws JsonProcessingException {
return mapper.writeValueAsString(obj);
}
}
class ThirdPartyPojo1 {
private String random1;
// public setters and getters
}
class ThirdPartyPojo2 {
private String random2;
// public setters and getters
}
class ourPojo {
private String random1;
private String random2;
//public setters and getters
}
Maven:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>

map only selected fields to the other object

I have two classes A and B, both classes have n number of fields. I need to map only specific fields from object of A to B using Streams API. I have only getters and setters in both classes and I don't have possibility of making changes in class A and B.
class A {
private String name;
private int age;
private String city;
}
class B {
private String name;
private String country;
}
I have a bunch of A object in an ArrayList. I need to create List of object B and the object should have only value for name field.
I have similar buisness use case, where I will be I will be having n number of fields and I need to map multiple fileds.
Below code which I have tried,
private List<B> mapAtoB(List<A> a) {
return a.stream().map(m -> mapToB(m)).collect(Collectors.toList());
}
private B mapToB(A a) {
B b = new B();
b.setName(a.getName());
return b;
}
Is there any best solution to achieve, other than the above impl or create and map using constructor.
Best way to map between classes is using a mapping library like MapStruct.
In case you still wanna do it manually then you can using reflection and FieldUtils from the Apache Commons Lang 3:
public List<Obj2> mapObj1toObj2(List<Obj1> source) {
return source.stream().map(obj1 -> {
Obj2 obj2 = new Obj2();
Arrays.asList(obj1.getClass().getDeclaredFields()).forEach(field -> {
try {
FieldUtils.writeField(obj2, field.getName(), FieldUtils.readField(field, obj1, true), true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return obj2;
}).collect(Collectors.toList());
}

Collectors.groupingBy with custom key name

I have class let's say CheeseMojo having various fields as follow:
public class CheeseMojo {
private String recipies = "Recipies";
private int age;
private String name;
private int submissionId;
//getter/setter
}
Now I want to sort it based on submissionId using Collectors.groupingBy function, then below code snippet will do the job
Map<Integer,List<CheeseMojo>> map = new HashMap<>();
map = cheeseMojos.stream().collect(Collectors.groupingBy(CheeseMojo::getSubmissionId));
And the output will be something like below:
{1=[CheeseMojo#111111],2=[CheeseMojo#222222]}
But I want my output something like below:
{"Recipies1"=[CheeseMojo#111111],"Recipies2"=[CheeseMojo#222222]}
Please help
Why don't you just map the key either before collecting (lets say by creating an instance of a wrapper class that has
public class CheeseMojoWrapper {
private final String id;
private final CheeseMojo wrapped;
public CheeseMojoWrapper(CheeseMojo toWrap) {
id = "Recipe" + toWrap.getId();
wrapped = toWrap;
}
}
or using a stream on the entrySet() of your resulting map...

Parsing Graphite JSON Response with Gson

I am writing a Java library for interacting with metrics from Graphite.
A typical JSON response looks like this (taken from the official docs):
[{
"target": "entries",
"datapoints": [
[1.0, 1311836008],
[2.0, 1311836009],
[3.0, 1311836010],
[5.0, 1311836011],
[6.0, 1311836012]
]
}]
where the first element of the "datapoints" array is the value and the second one the timestamp. I have modelled a GraphiteDataset class as follows
class GraphiteDataset {
private String target;
private List<GraphiteDatapoint> datapoints;
....
}
and the GraphiteDatapoint class
class GraphiteDatapoint {
private Long timestamp;
private Double value;
...
}
Now I need to parse the response (see above) into the GraphiteDataset
class using Gson. Unfortunately, the elements of "datapoints" are not named objects (e.g. {timestamp: 1234, value: 1.0} but a 2 dimensional array so I cannot directly deserialize it into some class. Currently my solution is to have an intermediate class
class GraphiteIntermediateDataset {
private String target;
private List<String> datapoints;
...
}
which has the datapoints as Strings and then I parse them into the appropriate GraphiteDatapoint instance. I think that I cannot work around a custom deserializer. Do you have any suggestions or tricks how to make this a little more convenient?
The JSON [1.2, 123456] is a array of a Double and a Long, but they are both Number, so try this:
class GraphiteDataset {
private String target;
private List<List<Number>> datapoints;
....
}
Then convert datapoints into your type after parsing, with something like:
List<GraphiteDatapoint> points = datapoints.stream().
.map(nums -> new GraphiteDatapoint(nums.get(0).doubleValue(), nums.get(1).intValue()))
.collect(Collectors.toList());
assuming a constructor like:
class GraphiteDatapoint {
private Long timestamp;
private Double value;
public GraphiteDatapoint(Double value, Long timestamp) {
this.value = value;
this.timestamp = timestamp;
}
...
}
The final solution is to introduce an intermediate class GraphiteIntermediateDataset which looks as follows:
class GraphiteIntermediateDataset {
private String target;
private List<List<Number>> datapoints;
}
and the deserializer code looks like this
List<GraphiteIntermediateDataset> intermediateDatasetList = GSON.fromJson(raw, new TypeToken<List<GraphiteIntermediateDataset>>(){}.getType());
GraphiteIntermediateDataset intermediateDataset = intermediateDatasetList.get(0);
... check if empty (which can happen), when true return an empty GraphiteDataset
List<GraphiteDatapoint> gDatapoints = intermediateDataset
.stream()
.map(ds -> {
return new GraphiteDatapoint(ds.get(0).longValue(),
ds.get(1).doubleValue())
}
.collect(Collectors.toList());
return new GraphiteDataset()
.setDatapoints(gDatapoints);
Type safety and proper data binding are your friends. Gson has several methods to accomplish what you need. For example, declare data transfer objects:
final class GraphiteDataset {
final String target;
// The incoming DTO has property `datapoints`, however Java conventions suggest dataPoints (as far as I understand English).
#SerializedName("datapoints")
final List<GraphiteDataPoint> dataPoints;
// Actually, Gson does not need this constructor, and the DTO can even have a single private default one.
// But in order to make it consistent with the next class just making it programmatically instantiable...
// Also, but may be opinion-based, hiding constructors is really a good idea since one can hide the instantiation strategy whilst constructors cannot.
private GraphiteDataset(final String target, final List<GraphiteDataPoint> dataPoints) {
this.target = target;
this.dataPoints = dataPoints;
}
}
final class GraphiteDataPoint {
final double value;
final long timestamp;
private GraphiteDataPoint(final double value, final long timestamp) {
this.value = value;
this.timestamp = timestamp;
}
// Instantiation must be accessible programmatically somehow
static GraphiteDataPoint graphiteDataPoint(final double value, final long timestamp) {
return new GraphiteDataPoint(value, timestamp);
}
}
And then implement either a GraphiteDataPoint JSON deserializer:
// In Gson serializers and deserializers can only deal with intermediate Gson JSON tree representation of objects (JsonElement-s).
// For some cases it's quite simple, if the given data to serialize/deserialize does not consume much memory
final class GraphiteDataPointJsonDeserializer
implements JsonDeserializer<GraphiteDataPoint> {
private static final JsonDeserializer<GraphiteDataPoint> graphiteDataPointJsonDeserializer = new GraphiteDataPointJsonDeserializer();
private GraphiteDataPointJsonDeserializer() {
}
// Not letting to instantiate a stateless (so it's thread-safe) deserializer twice or more
static JsonDeserializer<GraphiteDataPoint> getGraphiteDataPointJsonDeserializer() {
return graphiteDataPointJsonDeserializer;
}
#Override
public GraphiteDataPoint deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
final JsonArray asJsonArray = jsonElement.getAsJsonArray();
final double value = asJsonArray.get(0).getAsJsonPrimitive().getAsDouble();
final long timestamp = asJsonArray.get(1).getAsJsonPrimitive().getAsLong();
return graphiteDataPoint(value, timestamp);
}
}
Or a type adapter:
// Type adapters, unlike serializers and deserializers, are designed to work with streams.
// They may look too low-level and tedious/hard to implement, but for some cases they can be useful in both serialization and deserialization.
// For the case #1: no need to serialize nested objects recursively to transform them to JSON trees that can be important for large objects.
// For the case #2: intermediate JSON trees are not necessary (but internal buffers are).
final class GraphiteDataPointTypeAdapter
extends TypeAdapter<GraphiteDataPoint> {
private static final TypeAdapter<GraphiteDataPoint> graphiteDataPointTypeAdapter = new GraphiteDataPointTypeAdapter();
private GraphiteDataPointTypeAdapter() {
}
static TypeAdapter<GraphiteDataPoint> getGraphiteDataPointTypeAdapter() {
return graphiteDataPointTypeAdapter;
}
#Override
public void write(final JsonWriter out, final GraphiteDataPoint value) {
throw new UnsupportedOperationException("not implemented");
}
#Override
public GraphiteDataPoint read(final JsonReader in)
throws IOException {
in.beginArray();
final double value = in.nextDouble();
final long timestamp = in.nextLong();
in.endArray();
return graphiteDataPoint(value, timestamp);
}
}
Both implementations are essentially the same, but may be crucial for you dependening on data (de)serialization strategies and costs. Example use:
private static final String JSON = "[{\"target\":\"entries\",\"datapoints\":[[1.0,1311836008],[2.0,1311836009],[3.0,1311836010],[5.0,1311836011],[6.0,1311836012]]}]";
// Gson is thread-safe and can be shared between threads, so no need to instantiate it every time it's needed
private static final Gson gsonWithDeserializers = new GsonBuilder()
.registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointJsonDeserializer())
.create();
private static final Gson gsonWithTypeAdapters = new GsonBuilder()
.registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointTypeAdapter())
.create();
private static final TypeToken<List<GraphiteDataset>> graphiteDatasetsTypeToken = new TypeToken<List<GraphiteDataset>>() {
};
public static void main(final String... args) {
dumpGraphiteDatasets(gsonWithDeserializers.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
dumpGraphiteDatasets(gsonWithTypeAdapters.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
}
private static void dumpGraphiteDatasets(final Iterable<GraphiteDataset> graphiteDatasets) {
graphiteDatasets.forEach(graphiteDataset -> {
out.println(graphiteDataset.target);
graphiteDataset.dataPoints.forEach(graphiteDataPoint -> {
out.print(" ");
out.print(graphiteDataPoint.value);
out.print(" ");
out.println(graphiteDataPoint.timestamp);
});
});
}
The output:
entries
1.0 1311836008
2.0 1311836009
3.0 1311836010
5.0 1311836011
6.0 1311836012
entries
1.0 1311836008
2.0 1311836009
3.0 1311836010
5.0 1311836011
6.0 1311836012

Converting LinkedHashMap<String,MyClass> to Java Object

How can I convert the JSON string like this:
{ "summary": {
"totalMR":4.599000000000903E12,
"totalMA":1.9174920000386694E11,
"totalQA":5.1111111181E9,
"totalQR":1.000020666115264E11
},
"result": [{},{}],
"success":"true",
"total":49
}
to a Java object. I went through many similar posts and implemented constructors but couldn't find the proper explanation of why I'm unable to De-serialize the JSON.
Am I doing anything wrong?
My Class:
public class expResponse {
private String success;
private String total;
private ArrayList<LinkedHashMap<String,Object>> result;
private LinkedHashMap<String,SummaryResponse> summary;
// Constructor: public expResponse(){}
// Getter and Setter
}
public class SummaryResponse {
private Float totalQR;
private Float totalQA;
private Float totalMR;
private Float totalMA;
public SummaryResponse(){}
// Setter and Getter
}
My Code:
private expResponse processResult(String result) throws IOException{
ObjectMapper objectMapper = new ObjectMapper();
expResponse expResponseObj =
objectMapper.readValue(result, expResponse.class);
return expResponseObj;
The json you posted would not deserialize into a map of SummaryResponse objects, but rather an individual SummaryResponse object. To make your binding work, you would have to have json that looked something like this:
{
...
'summary': {
'summary1': {"totalMR":4.599000000000903E12,"totalMA":1.9174920000386694E11,"totalQA":5.1111111181E9,"totalQR":1.000020666115264E11}
'summary2': {"totalMR":4.599000000000903E12,"totalMA":1.9174920000386694E11,"totalQA":5.1111111181E9,"totalQR":1.000020666115264E11}
}
...
}
Alternatively, if you need to make your Java class conform to the json you provided, you simply need to change the declaration of summary:
private SummaryResponse summary;
Field summary in your json is an object of type SummaryResponse and not a LinkedHashMap.
public class ExpResponse {
private String success;
private String total;
private ArrayList<LinkedHashMap<String,Object>> result;
private Summary summary;
}
I don't think you have a problem in the code. Your input fails because it is not in the correct format. If you try to write the same values from an object with the same values to string you get something like:
{
"success":"true",
"total":"49",
"result":null,
"summary":{
"one_summary":{
"totalQR":2000.0,
"totalQA":1500.0,
"totalMR":1000.0,
"totalMA":500.0
}
}
}
And the major difference is the one summary. This is because summary is a map and maps need a key for each entryset. That means that summary is your map which has a one_summary key.
Is it the SummaryResponse that can't be deserialised?
I guess your attributes should have the same name "totalMR", "totalMA"....
or you should use an annotation JsonProperty(value="totalMR") and so on.

Categories

Resources