Jackson does not store xml values in object - java

I have an XML file like this:
"<?xml version ='1.0' encoding='UFT-8'?>"+
"<CLOSE_TRAILER>"+
"<user>" +
"<id>0600580</id>" +
"<trans_id>7</trans_id>" +
"<host_name>Mnull</host_name>"+
"<session_token>79430831Zahyy</session_token>" +
"<time_stamp>1551736026682</time_stamp>" +
"</user>"+
"<trailer_detail>" +
"<org_id>432</org_id>" +
"<trailer_prefix>XTRA</trailer_prefix>" +
"<system_type>SCAN</system_type>" +
"<truck_mail_flag>false</truck_mail_flag>" +
"<trailer_type_code>T</trailer_type_code>" +
"<in_contingency_flag>false</in_contingency_flag>" +
"<detl_code_svc_type>OVN</detl_code_svc_type>" +
"<catg_code_svc_type></catg_code_svc_type>" +
"<seal_number>12345</seal_number>" +
"<user_id>5155035</user_id>" +
"<trailer_number>24813</trailer_number>" +
"</trailer_detail>"+
"<load_handler>" +
"<employee_id>TEST</employee_id>" +
"<trailer_load_seq>71373839</trailer_load_seq>" +
"<action_code></action_code>" +
"</load_handler>"+
"</CLOSE_TRAILER>";
I want to map the trailer_detail and load_handler but not user, it into this object that I created for this xml:
#JsonRootName(value = "CLOSE_TRAILER")
public class CloseTrailerXml {
#JsonUnwrapped
Trailer trailer_detail;
#JsonUnwrapped
LoadHandler load_handler;
}
The Trailer class I have mapped like this:
#JsonRootName("trailer_detail")
public class Trailer {
private Integer equipmentSeq;
private String createdBy;
private ZonedDateTime createdDt;
private String updatedBy;
private ZonedDateTime updatedDt;
#JsonProperty("trailer_number")
private String trlrNbr;
... more member variables
In a unit test case I have the above xml stored as a string and I use this code below to map it into the object:
String msgNoNewLineChar = originalScannerMsg.replace("\\n","");
String msgNoCarReturnChar = msgNoNewLineChar.replace("\\r", "");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.setSerializationInclusion(Include.NON_NULL);
CloseTrailerXml closeTrailerXml= xmlMapper.readValue(originalScannerMsg, CloseTrailerXml.class);
The closeTrailerXml is created but all values are null.
If I remove the encoding line and CLOSE_TRAILER tag and the user section, it maps fine.
Why can't the object mapper work with the user section present?

Related

Saving List<T> as String

I've been trying to save a list of data as Strings, but when I try a method .toString on List, it returns address in memory instead of data.
public class Item {
Integer price = 20;
Integer modelNumber = 100;
String description = "Description";
String title = "Title";
Boolean wasBought = true;
}
public static void main(String[] args) {
List<Item> data = new ArrayList<>();
data.add(new Item());
System.out.println(data.toString());
}
You need to override toString function in your Item class. Add the following snippet into your Item class:
#Override
public String toString() {
return "Item{" +
"price=" + price +
", modelNumber=" + modelNumber +
", description='" + description + '\'' +
", title='" + title + '\'' +
", wasBought=" + wasBought +
'}';
}
Output:
[Item{price=20, modelNumber=100, description='Description', title='Title', wasBought=true}]
You can convert List to json format string by json utils, e.g. jackson or fastjson, in case you may need to convert it to Objects later.
Simply use Lombok (Add Lombok jar into classpath) #ToString annotate for Item class, it will do the needful output https://projectlombok.org/features/ToString

Reading from CSV file and create object

I'm a complete beginner to Java and I have been given an exercise where I have to read data from a CSV file and then create an object for each line of the file as the program reads the data from the file.
Here is part of the CSV file:
1,Jay, Walker,91 Boland Drive,BAGOTVILLE,NSW,2477
2,Mel, Lowe,45 Ocean Drive,MILLERS POINT,NSW,2000
3,Hugh, Manatee,32 Edgecliff Road,REDFERN,NSW,2016
4,Elizabeth, Turner,93 Webb Road,MOUNT HUTTON,NSW,2290
and so on ...
Here is my code that reads data from the CSV file:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Client_19918424 {
public static void main(String[] args) throws FileNotFoundException {
File inFile = new File("clients.txt");
Scanner inputFile = new Scanner(inFile);
String str;
String[] tokens;
while (inputFile.hasNext()) {
str = inputFile.nextLine(); // read a line of text from the file
tokens = str.split(","); // split the line using commas as delimiter
System.out.println("Client ID: " + tokens[0]);
System.out.println("Client First Name: " + tokens[1]);
System.out.println("Client Sur Name: " + tokens[2]);
System.out.println("Street Address: " + tokens[3]);
System.out.println("Suburb: " + tokens[4]);
System.out.println("State: " + tokens[5]);
System.out.println("Postcode:" + tokens[6]);
System.out.println( );
} // end while
}
}
this is my Client class (have constructor):
public class Client {
private int clientID;
private String firstName;
private String surName;
private String street;
private String suburb;
private String state;
private int postcode;
// constructor
public Client (int ID, String fName, String sName, String str, String sb, String sta, int pCode) {
clientID = ID;
firstName = fName;
surName = sName;
street = str;
suburb = sb;
state = sta;
postcode = pCode;
}
However I don't know how to create a Client object for each line of text file as the program reads data from file.
like for the first line make something like this:
Client client1 = new Client(1, "Jay", "Walker", "91 Boland Drive", "BAGOTVILLE", "NSW", 2477);
And then add it to array:
Client[0] = client1;
can someone help me to solve this question, im really appreciate.
You are almost there.
All that's left to do is to map each token that is already printed to the corresponding fields in the Client class. Since token[0] doesn't really tell what value it holds you could do it in three ways:
while (inputFile.hasNext()) {
str = inputFile.nextLine();
tokens = str.split(",");
// Because tokens[0] is of type String but clientID is of type int,
// we need to parse it and get the integer representation.
int clientID = Integer.parseInt(tokens[0]);
// Both of type String, no parsing required.
String firstName = tokens[1];
String surName = tokens[2];
String street = tokens[3];
String suburb = tokens[4];
String state = tokens[5];
int postcode = Integer.parseInt(tokens[6]);
// Then all that's left to do is to create a new object of `Client` type
// and pass all the gathered information.
Client client = new Client(clientID, firstName, surName, street, suburb, state, postcode);
System.out.println(client + "\n");
}
At this moment if we try to print the client (last line) we will get something like this: com.example.demo.Client#30a3107a. That's because we didn't tell how we want our object to be displayed. For that toString() method in Client class has to be overriden like so:
#Override
public String toString() {
return "Client ID: " + clientID + "\n" + "Client First Name: " + firstName + "\n"
+ "Client Sur Name: " + surName + "\n" + "Street Address: " + street + "\n"
+ "Suburb: " + suburb + "\n" + "State: " + state + "\n" + "Postcode: " + postcode;
}
It will give the exact output that is in your example.
It is achievable to create the class by passing those tokens directly as well, without the creation of temporary variables:
Client client = new Client(Integer.parseInt(tokens[0]), tokens[1], tokens[2], tokens[3], tokens[4], tokens[5], Integer.parseInt(tokens[6]));
This case brings us to the third solution with setters and getters.
The variables that describe the Client are already defined, it is possible to pass them to assemble the perfect object, but it is not possible to retrieve them. Instead of setting the variables directly in the constructor, we can create a special method that will do the job, for instance:
// Other fields omitted
private int clientID;
// The empty constructor required for later usage,
// since right now, we can't create the object without specifying every property.
public Client() {
}
// This method does exactly the same thing that was done before but
// in the constructor directly
public void setClientID(int clientID) {
this.clientID = clientID;
}
// This method will assist in retrieving the set data from above.
public int getClientID() {
return clientID;
}
And then the while loop would look like this instead:
Client client = new Client();
client.setClientID(Integer.parseInt(tokens[0]));
client.setFirstName(tokens[1]);
client.setSurName(tokens[2]);
client.setStreet(tokens[3]);
client.setSuburb(tokens[4]);
client.setState(tokens[5]);
client.setPostcode(Integer.parseInt(tokens[6]));
And to get those values:
System.out.println("Client ID: " + client.getClientID());
Or you could use the constructor with the fields to create the client, add getters in the class, omit both setters, and the empty constructor if the creation of the client should only be possible with all the fields present.

Convert HOCON string into Java object

One of my webservice return below Java string:
[
{
id=5d93532e77490b00013d8862,
app=null,
manufacturer=pearsonEducation,
bookUid=bookIsbn,
model=2019,
firmware=[1.0],
bookName=devotional,
accountLinking=mandatory
}
]
I have the equivalent Java object for the above string. I would like to typecast or convert the above java string into Java Object.
I couldn't type-cast it since it's a String, not an object. So, I was trying to convert the Java string to JSON string then I can write that string into Java object but no luck getting invalid character "=" exception.
Can you change the web service to return JSON?
That's not possible. They are not changing their contracts. It would be super easy if they returned JSON.
The format your web-service returns has it's own name HOCON. (You can read more about it here)
You do not need your custom parser. Do not try to reinvent the wheel.
Use an existing one instead.
Add this maven dependency to your project:
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.3.0</version>
</dependency>
Then parse the response as follows:
Config config = ConfigFactory.parseString(text);
String id = config.getString("id");
Long model = config.getLong("model");
There is also an option to parse the whole string into a POJO:
MyResponsePojo response = ConfigBeanFactory.create(config, MyResponsePojo.class);
Unfortunately this parser does not allow null values. So you'll need to handle exceptions of type com.typesafe.config.ConfigException.Null.
Another option is to convert the HOCON string into JSON:
String hoconString = "...";
String jsonString = ConfigFactory.parseString(hoconString)
.root()
.render(ConfigRenderOptions.concise());
Then you can use any JSON-to-POJO mapper.
Well, this is definitely not the best answer to be given here, but it is possible, at least…
Manipulate the String in small steps like this in order to get a Map<String, String> which can be processed. See this example, it's very basic:
public static void main(String[] args) {
String data = "[\r\n"
+ " {\r\n"
+ " id=5d93532e77490b00013d8862, \r\n"
+ " app=null,\r\n"
+ " manufacturer=pearsonEducation, \r\n"
+ " bookUid=bookIsbn, \r\n"
+ " model=2019,\r\n"
+ " firmware=[1.0], \r\n"
+ " bookName=devotional, \r\n"
+ " accountLinking=mandatory\r\n"
+ " }\r\n"
+ "]";
// manipulate the String in order to have
String[] splitData = data
// no leading and trailing [ ] - cut the first and last char
.substring(1, data.length() - 1)
// no linebreaks
.replace("\n", "")
// no windows linebreaks
.replace("\r", "")
// no opening curly brackets
.replace("{", "")
// and no closing curly brackets.
.replace("}", "")
// Then split it by comma
.split(",");
// create a map to store the keys and values
Map<String, String> dataMap = new HashMap<>();
// iterate the key-value pairs connected with '='
for (String s : splitData) {
// split them by the equality symbol
String[] keyVal = s.trim().split("=");
// then take the key
String key = keyVal[0];
// and the value
String val = keyVal[1];
// and store them in the map ——> could be done directly, of course
dataMap.put(key, val);
}
// print the map content
dataMap.forEach((key, value) -> System.out.println(key + " ——> " + value));
}
Please note that I just copied your example String which may have caused the line breaks and I think it is not smart to just replace() all square brackets because the value firmware seems to include those as content.
In my opinion, we split the parse process in two step.
Format the output data to JSON.
Parse text by JSON utils.
In this demo code, i choose regex as format method, and fastjson as JSON tool. you can choose jackson or gson. Furthermore, I remove the [ ], you can put it back, then parse it into array.
import com.alibaba.fastjson.JSON;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SerializedObject {
private String id;
private String app;
static Pattern compile = Pattern.compile("([a-zA-Z0-9.]+)");
public static void main(String[] args) {
String str =
" {\n" +
" id=5d93532e77490b00013d8862, \n" +
" app=null,\n" +
" manufacturer=pearsonEducation, \n" +
" bookUid=bookIsbn, \n" +
" model=2019,\n" +
" firmware=[1.0], \n" +
" bookName=devotional, \n" +
" accountLinking=mandatory\n" +
" }\n";
String s1 = str.replaceAll("=", ":");
StringBuffer sb = new StringBuffer();
Matcher matcher = compile.matcher(s1);
while (matcher.find()) {
matcher.appendReplacement(sb, "\"" + matcher.group(1) + "\"");
}
matcher.appendTail(sb);
System.out.println(sb.toString());
SerializedObject serializedObject = JSON.parseObject(sb.toString(), SerializedObject.class);
System.out.println(serializedObject);
}
}

OpenCSV flat to hierarchy structure data parsing

I need advice on the issue which I am facing while parsing CSV data in java.
I have a CSV file with data in below format
name, gender, address_first_line, address_second_line, city, number
me, Male, anonymous, random, Auckland, 6545
other, Female, random, anon, Auckland, 6688
I want to parse the CSV file using the openCSV library in below Object Model. I am aware of parsing single class but facing issues while parsing data when multiple classes are involved. Please suggest if there is any other library available which can help me to get the desired result.
Class User {
String name;
String gender;
Address address;
long number;
}
Class Address {
String firstLine;
String secondLine;
}
univocity-parsers has a #Nested annotation you can use:
Try this:
public static class User {
#Parsed
String name;
#Parsed
String gender;
#Nested
Address address;
#Parsed
long number;
#Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", address=" + address +
", number=" + number +
'}';
}
}
public static class Address {
#Parsed(field = "address_first_line")
String firstLine;
#Parsed(field = "address_second_line")
String secondLine;
#Override
public String toString() {
return "Address{" +
"firstLine='" + firstLine + '\'' +
", secondLine='" + secondLine + '\'' +
'}';
}
}
public static void main(String ... args){
StringReader input = new StringReader(
"name,gender,address_first_line,address_second_line, city,number\n" +
"John,M,Somewhere,Else,City,1");
for(User user : new CsvRoutines().iterate(User.class, input)){
System.out.println(user.toString());
}
}
Output:
User{name='John', gender='M', address=Address{firstLine='Somewhere', secondLine='Else'}, number=1}
If you don't like annotations you can map things manually:
CsvRoutines routines = new CsvRoutines();
ColumnMapper mapper = routines.getColumnMapper();
mapper.attributeToColumnName("name", "name");
mapper.attributeToColumnName("gender", "gender");
mapper.attributeToColumnName("number", "number");
mapper.attributeToColumnName("address.firstLine", "address_first_line");
mapper.attributeToColumnName("address.secondLine", "address_second_line");
for (User user : routines.iterate(User.class, input)) {
System.out.println(user.toString());
}
Hope it helps.
Disclaimer: I'm the author of this lib. It's open-source and free (Apache 2.0 license)

jackson deserialize array into a java object

I have json'ed array of host, port, uri tuples encoded by 3rd party as an array of fixed length arrays:
[
["www1.example.com", "443", "/api/v1"],
["proxy.example.com", "8089", "/api/v4"]
]
I would like to use jackson magic to get a list of instances of
class Endpoint {
String host;
int port;
String uri;
}
Please help me to put proper annotations to make ObjectMapper to do the magic.
I do not have control on the incoming format and all my google'n ends in answers on how to map array of proper json objects (not arrays) into list of objects (like https://stackoverflow.com/a/6349488/707608)
=== working solution as advised by https://stackoverflow.com/users/59501/staxman
in https://stackoverflow.com/a/38111311/707608
public static void main(String[] args) throws IOException {
String input = "" +
"[\n" +
" [\"www1.example.com\", \"443\", \"/api/v1\"],\n" +
" [\"proxy.example.com\", \"8089\", \"/api/v4\"]\n" +
"]";
ObjectMapper om = new ObjectMapper();
List<Endpoint> endpoints = om.readValue(input,
new TypeReference<List<Endpoint>>() {});
System.out.println("endpoints = " + endpoints);
}
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
static class Endpoint {
#JsonProperty() String host;
#JsonProperty() int port;
#JsonProperty() String uri;
#Override
public String toString() {
return "Endpoint{host='" + host + '\'' + ", port='" + port + '\'' + ", uri='" + uri + '\'' + '}';
}
}
Add following annotation:
#JsonFormat(shape=JsonFormat.Shape.ARRAY)
class Endpoint {
}
and it should serialize entries as you wish.
Also: it'd be safest to then use #JsonPropertyOrder({ .... } ) to enforce specific ordering, as JVM may or may not expose fields or methods in any specific order.
Consider importing the two dimensional array and then running through a loop. Then you can have a constructor on the Endpoint class that accepts each instance of internal array.

Categories