import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class clsWarehouse {
public static void main(String[] args){
class Items {
private String iItemID;
private String strItemName;
private String strItemDescription;
private String iItemPrice;
private String strSize;
private String strSex;
public Items (String id, String name, String description, String price, String size, String sex){
iItemID = id;
strItemName = name;
strItemDescription = description;
iItemPrice = price;
strSize = size;
strSex = sex;
}
}
Map<Integer, Items> ItemMap = new HashMap<Integer, Items>();
ItemMap.put(1, new Items("3", "test1", "test2", "30", "20", "Male"));
for (Map.Entry<Integer, Items> entry : ItemMap.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
Items test = entry.getValue();
String test2 = test.toString();
System.out.println(test2);
}
}
}
Hello,
I want to get the value stored in the map but i get only the memory locations instead, Key = 1, Value = clsWarehouse$1Items#c3c749. Can someone help me with this.
Thanks!
Override the toString() method in your Items class so that when you call it, you control the output. The default implementation in Object produces the behavior you are observing (from the linked Javadocs)
getClass().getName() + '#' + Integer.toHexString(hashCode())
The hashCode() method, by default, uses the memory address.
add the getters that you need in your Items class
Ex (for ItemID) :
public String getItemID()
{
return iItemID;
}
and in your for loop
Items test = entry.getValue();
System.out.println(test.getItemID());
Related
I am developing an application within that I have to create the SHA256 hash for the incoming data. To make that I have to follow the specific sorting order for each property. Hence, I have created a class TemplateNodeMap which extends LinkedHashMap, within that I have specified the order that I need to follow.
Now, I would like to read each property in the incoming JSON data, add a specific field, and create the Hash string. I am a bit confused about adding the data and creating the string. I am worried if I am following the optimal procedure or not as I need to follow the process for a large amount of data.
Can someone please let me know if this is the right approach?
Following is the incoming JSON (Since JSON can have any order I need to obtain properties according to my required Hash String order):
{
"age": 30,
"name": "Batman",
"address": {
"city": "Gotham",
"street": {
"name": "Gotham 123"
}
}
}
Following is my TemplateNodeMap class:
package io.hash;
import java.util.LinkedHashMap;
public class TemplateNodeMap extends LinkedHashMap {
public TemplateNodeMap() {
put("name", null);
put("age", null);
put("address", new LinkedHashMap<>() {{
put("street", new LinkedHashMap<>() {{
put("name", null);
}});
put("city", null);
}});
}
}
Following is my ApplicationMain class which is reading and loading the data to TemplateNodeMap:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
public class ApplicationMain {
public static void main(String[] args) throws IOException {
final ObjectMapper objectMapper = new ObjectMapper();
final InputStream jsonStream = ApplicationMain.class.getResourceAsStream("/InputJSON.json");
final ObjectNode inputTemplate = objectMapper.readValue(jsonStream, ObjectNode.class);
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inputTemplate));
final TemplateNodeMap templateNodeMap = new TemplateNodeMap();
templateNodeMap.put("name", inputTemplate.get("name"));
templateNodeMap.put("age", inputTemplate.get("age"));
//Unable to understand how to insert the complex object values into LinkedHashMap and follow the order
}
}
I am not understanding how to add the complex object to LinkedHashMap and create a string out of it.
Not all fields are mandatory so I would like to omit the null values during the creation of Hash String.
Can someone please suggest to me how to achieve this and if this is the right approach to creating a Hash String based on the required order?
There are two jackson annotations that can help you to serialize the jackson properties in a custom order excluding the non null values:
JsonPropertyOrder annotation that can be used to define ordering (possibly partial) to use when serializing object properties.
JsonInclude annotation used to indicate when value of the annotated property, or all properties of the annotated class, is to be serialized.
You can then deserialize your json to the pojo java classes and then serialize them obtaining a new json with the expected order of the properties and without null properties:
#Data
#JsonPropertyOrder(value = {"name", "age", "address"})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
private int age;
private String name;
private Address address;
}
#Data
#JsonPropertyOrder(value = {"street", "city"})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Address {
private String city;
private Street street;
}
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Street {
private String name;
}
//you can delete for example the name property from your json
Person person = mapper.readValue(json, Person.class);
String output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
//ok, the name property will not appear in the output because it's null
System.out.println(output);
Requirements:
"I am developing an application within that I have to create the SHA256 hash for the incoming data."
"To make that I have to follow the specific sorting order for each property."
Proposal:
Create data classes for your incoming data, order their attributes as you like
Transform data class into a 'standard' JSON representation using prettyprint
Calculate hash over 'standard' JSON representation
For completeness a manual parsing of the linked hashmap is included
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
class Person {
int age; String name; Address address; // Attribute order effects output order!
public Person(){}
public Person(int age, String name, Address address) {
this.age = age; this.address = address; this.name = name;
}
public void setAge(int age){this.age = age;}
public int getAge(){return age;}
public void setName(String name){this.name = name;}
public String getName(){return name;}
public void setAddress(Address address){this.address = address;}
public Address getAddress(){return address;}
}
class Address {
String city;
Street street;
public Address(){}
public Address(String city, Street street){this.city = city; this.street = street;}
public void setCity(String city){this.city = city;}
public String getCity(){return city;}
public void setStreet(Street street){this.street = street;}
public Street getStreet(){return street;}
}
class Street {
String name;
public Street(){}
public Street(String name) {this.name = name;}
public String getName(){return name;}
public void setName(String name) {this.name = name;}
}
public class ApplicationMain {
static String inputJson = "{\"age\": 30,\"name\": \"Batman\",\"address\": {\"city\": \"Gotham\",\"street\": {\"name\": \"Gotham 123\"}}}";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
final ObjectMapper objectMapper = new ObjectMapper();
try {
// use Paths.get("InputJSON.json").toFile() as alternative to the string
Map<?, ?> map = objectMapper.readValue(inputJson, Map.class);
System.out.println("The linked hashmap to process for sorting:");
System.out.println(map);
System.out.println("Individual elements:");
System.out.println("name: " + map.get("name"));
System.out.println("age: " + map.get("age"));
System.out.println("address:");
Map<?, ?> addressMap = (Map<?, ?>)map.get("address");
System.out.println(" city: " + addressMap.get("city"));
System.out.println(" street:");
System.out.println(" name: " + ((Map<?, ?>)addressMap.get("street")).get("name"));
} catch (Exception ex) {
ex.printStackTrace();
}
Person person = objectMapper.readValue(inputJson, Person.class);
System.out.println("Original JSON:\n" + inputJson);
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
System.out.println("Standardized JSON:\n" + prettyJson);
byte[] hash = MessageDigest.getInstance("SHA-256").digest(prettyJson.getBytes(StandardCharsets.UTF_8));
for ( byte b : hash) {
System.out.printf("%02x", b);
}
System.out.println();
}
}
$ java -cp .:jackson-databind-2.13.3.jar:jackson-core-2.13.3.jar:jackson-annotations-2.13.3.jar ApplicationMain
The linked hashmap to process for sorting:
{age=30, name=Batman, address={city=Gotham, street={name=Gotham 123}}}
Individual elements:
name: Batman
age: 30
address:
city: Gotham
street:
name: Gotham 123
Original JSON:
{"age": 30,"name": "Batman","address": {"city": "Gotham","street": {"name": "Gotham 123"}}}
Standardized JSON:
{
"age" : 30,
"name" : "Batman",
"address" : {
"city" : "Gotham",
"street" : {
"name" : "Gotham 123"
}
}
}
5f962abf0cdc5150eb2614c629592d889d59b82499b13dd1b8d12e421bb0440a
$
You can check the hash value at https://dencode.com/hash
(The link already contains the JSON data. If you paste text there, verify that the correct line ending type is selected.)
I try to inject a string to a list using stream, but only inject the first row.
Input:
1, Kelvin, 1
2, kelvin, 1
3, John, 1
Expected:
1, Kelvin, 2, Alvin
3, John, 1, Alvin
Result:
1, Kelvin, 2 Alvin
3, John, 1, null
Code:
String lastName = "Alvin"
Map<Object, Person> map = list.stream().collect(Collectors.toMap(
f -> f.getCode() + f.getName(),
Function.identity(),
(s, a) -> new Person(s.getCode(), s.getName, s.getAmount(), lastName)));
The issue is that you're injecting lastName in mergeFunction parameter of Collectors.toMap function, instead of valueMapper parameter.
Collectors.toMap takes 3 arguments:
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction
keyMapper will generate a key for your entry in map,
valueMapper will generate a value for your entry in map,
mergeFunction is function which is called, when the value mapping function is applied to equal element (Object.equals()), so both values are merged using the provided merging function.
You should provide correct mapping function as valueMapper argument. Provided by you Function.identity() will always return original input parameter:
Returns a function that always returns its input argument.
Type parameters:
<T> – the type of the input and output objects to the function
Returns:
a function that always returns its input argument
static <T> Function<T, T> identity() {
return t -> t;
}
Apparently you mixed up the valueMapper and mergeFunction of the Collectors.toMap. Here is my best guess with the information given:
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.function.Function;
public class Mapper {
public class Person {
private Integer code;
private String name;
private String lastName;
public Person(Integer code, String name, String lastName) {
this.code = code;
this.name = name;
this.lastName = lastName;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
public String getLastName() {
return lastName;
}
public String toString() {
return this.code + ", " + this.name + ", " + this.lastName;
}
}
public static void main(String []args) {
new Mapper().run();
}
public void run() {
List<Person> persons = new ArrayList<>();
persons.add(new Person(1, "Kelvin", null));
persons.add(new Person(2, "Alvin", null));
persons.add(new Person(3, "John", null));
String lastName = "Alvin";
Map<Object, Person> map = persons.stream().collect(Collectors.toMap(
f -> f.getCode() + f.getName(),
p -> new Person(p.getCode(), p.getName(), lastName)));
for (Map.Entry<Object, Person> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
Output:
1Kelvin = 1, Kelvin, Alvin
2Alvin = 2, Alvin, Alvin
3John = 3, John, Alvin
While there already are good answers I just wanted to provide my answer with a different approach to a solution.
Instead of using the valueMapper of the Collector, I used the map function of the stream with the adjustment of using a builder pattern inside of the Person pojo to aid in the usability for this.
Please note that this approach will only work if the builder pattern is used and the class itself is returned by the result of the map operation.
Run it and see it working with Ideone: https://ideone.com/WFcZCo
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
List<Person> personList = new ArrayList<>();
Person kelvin = new Person("1","Kelvin");
Person alvin = new Person("2","Alvin");
Person john = new Person("3","John");
personList.add(kelvin);
personList.add(alvin);
personList.add(john);
String lastName = "Alvin";
Map<Object, Person> map = personList.stream()
.map(p -> p.setLastName(lastName))
.collect(Collectors.toMap(
f -> f.getCode() + f.getName(),
Function.identity()
));
System.out.println("Result:");
for(Map.Entry<Object, Person> entry : map.entrySet()){
Object key = entry.getKey();
Person person = entry.getValue();
System.out.println(person);
}
}
}
class Person {
private String code;
private String name;
private String lastname;
public Person(String code, String name){
this.code = code;
this.name = name;
}
public Person(String code, String name, String lastname){
this.code = code;
this.name = name;
this.lastname = lastname;
}
public String getCode(){
return code;
}
public String getName(){
return name;
}
public String getLastname(){
return lastname;
}
public Person setLastName(String lastname){
this.lastname = lastname;
return this;
}
public String toString(){
return code + ", " + name + ", " + lastname;
}
}
And here the result:
Result:
1, Kelvin, Alvin
2, Alvin, Alvin
3, John, Alvin
I have a map in java which has String as Key and an integer list as value. My query returns the below set
"Day:1, Day:2, Day:3, Month:1, Year:15, Year:20, Year:25"
Meaning, I have keys and different values for each keys in random.. I am expecting an output of a map which has unique key and corresponding list of int in values as seen below:
{
"day": [1,2,3],
"Month": [1],
"year": [15,20,25]
}
Kindly help
public static void main(String[] args){
String input = "Day:1, Day:2, Day:3, Month:1, Year:15, Year:20, Year:25";
List<String> list = Arrays.asList(input.split(","));
JSONObject jsonObject = new JSONObject();
for (String val : list) {
String[] splitedValues = val.split(":");
if(jsonObject.has(splitedValues[0].trim())) {
jsonObject.getJSONArray(splitedValues[0].trim()).put(Integer.valueOf(splitedValues[1]));
}else {
List<Integer> integers = new ArrayList<Integer>();
integers.add(Integer.valueOf(splitedValues[1]));
jsonObject.put(splitedValues[0].trim(), integers);
}
}
System.out.println(jsonObject);
}
output
{"Month":[1],"Year":[15,20,25],"Day":[1,2,3]}
Please find below code. I have created an inner class to map type and value and then collected it by grouping the type.
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
public class StringToMap {
public static void main(String[] args) {
String queryOutput = "Day:1, Day:2, Day:3, Month:1, Year:15, Year:20, Year:25";
System.out.println(groupByTypeAndAddValuesToList(queryOutput));
}
private static Map<String, List<Integer>> groupByTypeAndAddValuesToList(String queryOutput) {
final String[] split = queryOutput.split(",");
return Arrays.stream(split).map(s -> {
final String trimmed = s.trim();
final String[] splitByColon = trimmed.split(":");
return new TypeValues(splitByColon[0], Integer.parseInt(splitByColon[1]));
}).collect(groupingBy(TypeValues::getType, Collectors.mapping(TypeValues::getValue, toList())));
}
private static class TypeValues {
private final String type;
private final Integer value;
public TypeValues(String type, Integer value) {
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public Integer getValue() {
return value;
}
}
}
I have a list of objects where a few records can have empty value property and a few can have null value property. Using Collectors.groupingBy I need both the records to be considered as same.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Code {
private String type;
private String description;
public static void main(String[] args) {
List<Code> codeList = new ArrayList<>();
Code c = new Code();
c.setDescription("abc");
c.setType("");
codeList.add(c);
Code c1 = new Code();
c1.setDescription("abc");
c1.setType(null);
codeList.add(c1);
Map<String, List<Code>> codeMap = codeList.stream()
.collect(Collectors.groupingBy(code -> getGroupingKey(code)));
System.out.println(codeMap);
System.out.println(codeMap.size());
}
private static String getGroupingKey(Code code) {
return code.getDescription() +
"~" + code.getType();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The result of codeMap will have two records since it considers the empty string and the null value in the Type property as different. How can I achieve getting a single record here by considering both the null and empty records as same.
You can modify your getGroupingKey method like this:
private static String getGroupingKey(Code code) {
return code.getDescription() + "~" + (code.getType() == null ? "" : code.getType());
}
or like this:
private static String getGroupingKey(Code code) {
return code.getDescription() + "~" + Optional.ofNullable(code.getType()).orElse("");
}
or you might as well modify your getType() method directly as in:
public String getType() {
return type == null ? "" : type;
}
or:
public String getType() {
return Optional.ofNullable(type).orElse("");
}
Either should work the same. Pick one depending on your requirements I guess..
If you add the following toString method to your Code class:
#Override
public String toString() {
return "Code{" +
"type='" + type + '\'' +
", description='" + description + '\'' +
'}';
}
.. with the modified getGroupingKey method (or the getType method) the output should be as follows:
{abc~=[Code{type='', description='abc'}, Code{type='null', description='abc'}]}
1
Edit: You can also considering initializing the type to an empty String instead of null, then you would not need to modify anything:
private String type = "";
That might be an option too..
Suppose I have the following JSON that I'm unable to alter as it's provided over a web feed by someone else. I want to parse this using Jackson to Java objects.
{
"2002": [
{
"d": "description",
"t": "title"
}
],
"2003": [
{
"d": "description",
"t": "title"
}
]
}
The data represents, say, a list of TV programmes with ids=2002, 2003 ...etc, and each programme has a description and title. I want to parse this data into a List of generic Programme classes, where each programme class has fields d and t. I don't want to have separate classes for 2002, 2003 etc objects. Bearing in mind, the 2002, 2003 etc ids are not known until runtime and can evolve over time, and could also be quite a long list of possible values.
Is it possible to model this as a list of generic programmes whose id field is equal to the name of the object name from the json string? In other words, I don't want this:
public class AllProgrammes {
private List<com.example._2002> _2002;
private List<com.example._2003> _2003;
// getters and setters
}
but instead this should just contain List<Programmes>, and each programme object should have an id = 2002, or 2003, or whatever id it is.
Thanks.
If you can use Google Gson, you can do that this way:
Program.class
public class Program {
private String id;
private String title;
private String description;
public Program(String id, String title, String description) {
this.id = id;
this.title = title;
this.description = description;
}
#Override
public String toString() {
return String.format("Program[id=%s, title=%s, description=%s]", this.id, this.title, this.description);
}
}
ProgramsDeserializer.class
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
class ProgramsDeserializer implements JsonDeserializer<List<Program>> {
#Override
public List<Program> deserialize(JsonElement e, Type type, JsonDeserializationContext jdc) throws JsonParseException {
List<Program> programs = new ArrayList<>(10);
JsonObject root = e.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : root.entrySet()) {
String id = entry.getKey();
String title = "";
String description = "";
JsonElement arrayElement = entry.getValue();
if (arrayElement.isJsonArray()) {
JsonArray array = arrayElement.getAsJsonArray();
JsonElement objectElement = array.get(0);
if (objectElement.isJsonObject()) {
JsonObject object = objectElement.getAsJsonObject();
title = object.get("t").getAsString();
description = object.get("d").getAsString();
}
}
programs.add(new Program(id, title, description));
}
return programs;
}
}
GsonExample.class
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GsonExample {
private static final Logger logger = Logger.getLogger(GsonExample.class.getName());
private static final String JSON =
"{"
+ "\"2002\": ["
+ "{"
+ "\"d\": \"description\","
+ "\"t\": \"title\""
+ "}"
+ "],"
+ "\"2003\": ["
+ "{"
+ "\"d\": \"description\","
+ "\"t\": \"title\""
+ "}"
+ "]"
+ "}";
public static void main(String[] args) {
GsonExample e = new GsonExample();
e.run();
}
private void run() {
GsonBuilder builder = new GsonBuilder();
Type type = new TypeToken<List<Program>>(){}.getType();
builder.registerTypeAdapter(type, new ProgramsDeserializer());
Gson gson = builder.create();
List<Program> programs = gson.fromJson(JSON, type);
logger.log(Level.INFO, "{0}", programs);
}
}