Deserialize string to object - java

What is the best way to convert this format of string to object?
[Start successful - User:Berord; Name:Test; Fruits:Orange; Version:;]
I'm thinking to split it with ';' and do substring(str.indexOf("User:") + 1 ), substring(str.indexOf("Name:") + 1 )
It is still have any other better method?

If the user data always is in the form FieldNameWithoutSpaces:DataWithOutSemicolon; you could use the following regex along with Pattern and Matcher: (\S+):([^;]*); and extract groups 1 and 2 from each match.
Example:
Pattern p = Pattern.compile("(\\S+):([^;]*);");
Matcher m= p.matcher( "[Start successful - User:Berord; Name:Test; Fruits:Orange; Version:;]" );
while( m.find() ) {
String key = m.group( 1 );
String value = m.group( 2 );
//put those into a map or use in some other way, for demonstration I'll print them
System.out.println( key + "::" + value );
}
This would result in the following output.
User::Berord
Name::Test
Fruits::Orange
Version::
Note that the key should not contain whitespace but should be preceeded by at least one, otherwise you'd match much more, i.e. if your input was [Start successful-User:Berord;Name:Test;Fruits:Orange; Version:;] you'd get key = " successful-User:Berord;Name:Test;Fruits" andvalue = "Orange". In the same way your values should not contain semicolons or the matches would get messed up as well.
If you have the requirement that spaces need to be optional and values could contain semicolons the regex might get a lot more complex (depends on the requirements) or even unsuitable at all - in which case you'd need to use (write) a more specialized parser.

I would go step by step splitting this String:
Split the input by minus (-) in order to access the interesting part only
Split this interesting part by semicolon (;) to get the key-value pairs
Split each key-value pair by colon (:) to get the key and the value separately
As someone already mentioned in a comment, you can use a Map<String, String> to store the key-value pairs.
There are several optimizations done in the following code, such as trim() to eliminate leading and trailing whitespaces from keys and values, some (possibly obsolete) checks for existence of a key in the map and some checks for the results of splitting operations.
Here is an example solution, read the comments:
public static void main(String args[]) {
String s = "Start successful - User:Berord; Name:Test; Fruits:Orange; Version:;";
// provide a data structure that holds the desired key-value pairs
Map<String, String> kvMap = new HashMap<>();
// (#1) split the input by minus ('-') to get rid of the leading part
String[] splitByMinus = s.split("-");
// check if the result length is the expected one
if (splitByMinus.length == 2) {
// (#2) take the desired part and split it again, this time by semicolon (';')
String[] splitBySemicolon = splitByMinus[1].split(";");
for (String ss : splitBySemicolon) {
// (#3) split the result of the splitting by semicolon another time, this time
// by a colon (':')
String[] splitByColon = ss.split(":");
// again, check the length of the result
if (splitByColon.length == 2) {
// if yes, you have successfully extracted the key and value, erase leading and
// trailing spaces
String key = splitByColon[0].trim();
String value = splitByColon[1].trim();
// check if the map contains the key
if (kvMap.containsKey(key)) {
// YOU DECIDE: skip this entry or update the existing key with the new value
System.out.println("There already is a key " + key + ". What now?");
} else {
// if the map doesn't have the key, insert it along with the value
kvMap.put(key, value);
}
} else if (splitByColon.length == 1) {
System.out.println(
"There was a key or value missing the corresponding key or value: \""
+ splitByColon[0].trim()
+ "\" (splitting by colon resulted in only one String!)");
// for the moment, we regard this case as a key without a value
String key = splitByColon[0].trim();
// check if the map contains the key
if (kvMap.containsKey(key)) {
// YOU DECIDE: skip this entry or update the existing key with the new value
System.out.println("There already is a key " + key
+ ". What now? This time there is no new value, "
+ "so skipping this entry seems a good idea.");
// do nothing...
} else {
// if the map doesn't have the key, insert it along with the value
kvMap.put(key, null);
}
} else {
System.err.println("Splitting by colon resulted in an unexpected amount of Strings,"
+ "here " + splitByColon.length);
}
}
} else {
System.err.println("Splitting the input String resulted in an unexpected amount of parts");
}
// finally print your results that are stored in the map:
kvMap.forEach((key, value) -> System.out.println(key + " : " + (value == null ? "" : value)));
}
The result I get from it is
There was a key or value missing the corresponding key or value: "Version" (splitting by colon resulted in only one String!)
User : Berord
Version :
Name : Test
Fruits : Orange

Split with ; first you will get array of string with size of 3 and you can loop them over and split that with :
String s = "Start successful - User:Berord; Name:Test; Fruits:Orange; Version:;".replace("Start successful -","");
String splits[]=s.split(";");
for(String split:splits)
{
String splittedStrings[] =split.split(":");
String key = splittedStrings[0];
String value = splittedStrings[1];
}

Related

Best way to retrieve a value from a string java

If I am being passed a string that contains comma delimited key-value pairs like this
seller=1000,country="canada",address="123 1st st", etc.
There seems like there must be a better way than parsing then iterating through.
What is the best way to retreive a value from this string based on the key name in Java?
You can create your own CSV parser, it's not very complicated but there are a few corner cases to be carfull with assuming of course you are using standard CSV format.
But why reinventing the wheel...
You can try looking up a CSV parser like
OpenCSV
SuperCSV
Apache Commons
There are others, look around I'm sure you will find one that suits your needs.
Since release 10 Google Guava provides a class MapSplitter which does exactly that kind of things:
Map<String, String> params = Splitter
.on(",")
.withKeyValueSeparator("=")
.split("k1=v1,k2=v2");
Usually you will want to parse the string into a map because you will be pulling various values perhaps multiple times, so it often makes sense to pay the parsing cost up-front.
If not, then here is how I would solve the problem (assuming you want to differentiate between int values and String values).:
public Object pullValue(String pairs, String key) {
boolean returnString = false;
int keyStart = pairs.indexOf(key + "=");
if (keyStart < 0) {
logger.error("Key " + key + " not found in key-value pairs string");
return null;
}
int valueStart = keyStart + key.length() + 1;
if (pairs.charAt(valueStart) == '"') {
returnString = true;
valueStart++; // Skip past the quote mark
}
int valueEnd;
if (returnString) {
valueEnd = pairs.indexOf('"', valueStart);
if (valueEnd < 0) {
logger.error("Unmatched double quote mark extracting value for key " + key)
}
return pairs.substring(valueStart, valueEnd);
} else {
valueEnd = pairs.indexOf(',', valueStart);
if (valueEnd < 0) { // If this is the last key value pair in string
valueEnd = pairs.length();
}
return Integer.decode(pairs.substring(valueStart, valueEnd));
}
}
Note that this solution assumes no spaces between the key, the equals sign, and the value. If these are possible you will have to create some code to travel the string between them.
Another solution is to use a regular expression parser. You could do something like (this is untested):
Pattern lookingForString = Pattern.compile(key + "[ \t]*=[ \t]*[\"]([^\"]+)[\"]");
Pattern lookingForInt = Pattern.compile(key + "[ \t]*=[ \t]*([^,]+)");
Matcher stringFinder = lookingForString.matcher(pairs);
Matcher intFinder = lookingForInt.matcher(pairs);
if (stringFinder.find()) {
return stringFinder.group(1);
} else if (intFinder.find()) {
return Integer.decode(intFinder.group(1));
} else {
logger.error("Could not extract value for key " + key);
return null;
}
HTH
To separate the string by commas, the other posters are correct. It is best to use a CSV parser (your own or OTS). Considering things like commas inside quotes etc can lead to a lot of un-considered problems.
Once you have each separate token in the form:
key = "value"
I think it is easy enough to look for the first index of '='. Then the part before that will be the key, and the part after that will be the value. Then you can store them in a Map<String, String>.
This is assuming that your keys will be simple enough, and not contain = in them etc. Sometimes it's enough to take the simple route when you can restrict the problem scope.
If you just want one value out of such a string, you can use String's indexOf() and substring() methods:
String getValue(String str, String key)
{
int keyIndex = str.indexOf(key + "=");
if(keyIndex == -1) return null;
int startIndex = str.indexOf("\"", keyIndex);
int endIndex = str.indexOf("\"", startIndex);
String value = str.substring(startIndex + 1, endIndex);
return value;
}
First thing you should use a CSV parsing library to parse the comma separated values. Correctly parsing CSV data isn't as trivial as it first seems. There are lots of good arguments to not reinvent that wheel.
This will also future proof your code and be code you don't have to test or maintain.
I know the temptation to do something like data.split(','); is strong, but it is fragile and brittle solution. For just one example, what if any of the values contain the ','.
Second thing you should do is then parse the pairs. Again the temptation to use String.split("="); will be strong, but it can be brittle and fragile if the right hand side of the = has an = in it.
I am not a blind proponent of regular expressions, but used with restraint they can be just the right tool for the job. Here is the regular expression to parse the name value pairs.
The regular expression ^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$, click the regular expression to test it online interactively. This works even for multiple double quotes in the right hand side of the name value pair.
This will match only what is on the left side of the first = and everything else on the right hand side, and strip the optional " off the string values, while still matching the non-quoted number values.
Given a List<String> list of the encoded name value pairs.
final Pattern p = Pattern.compile("^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$");
final Map<String, String> map = new HashMap<String, String>(list.size());
for (final String nvp : list)
{
final Matcher m = p.matcher(nvp);
m.matches();
final String name = m.group(1);
final String value = m.group(2);
System.out.format("name = %s | value = %s\n", name, value);
}
Use String.split(yourdata, ',') and you will get a String[]. Then, perform String.split(String[i],"="), on each entry to separate property and values.
Ideally, you should move this data in a Properties object instance. You can then save/load it from XML easily. It has useful methods.
REM: I am assuming that you are savvy enough to understand that this solution won't work if values contain the separator (i.e., the comma) in them...

How to get specific word in a string in Java [duplicate]

This question already has an answer here:
Java Best way to extract parts from a string
2 answers
Getting a substring from a string after a particular word
3 answers
I have the following texts:
"The data of Branch 1 are correct - true"
"data of Branch 4 are correct - false"
For each text, I would like to get the number of branch and the boolean value either true or false. The outcome for each line will be:
1 true
4 false
How can I do that?
You can do it in different manner.
One of the simplest in this case is split the string and getting the fifth and ninth element
String sentence = "The data of Branch 1 are correct - true";
String[] elements = string.split(" "); then get relevant elements of array.
// elements[4] is the string 1 (you can convert it to int if necessary with Integer.parseInt(elements[4]) )
// elements[8] is the string true (you can convert it to boolean if necessary with Boolean.parseBoolean(elements[4]) )
Other possibilities are:
Using regular expressions (find the number and search the words true false)
Using the position (you know that the number starts always in the same position and that the boolean value is always at the end)
Knowing that you can create a method similar to the following to print the relevant parts:
public static void printRelevant(String string) {
String[] elements = string.split(" ");
System.out.println(elements[4] + " " + elements[8]);
}
...
pritnRelevant("The data of Branch 1 are correct - true");
printRelevant("The data of Branch 4 are correct - false");
Thanks to the comments of Sotirios I saw that the 2 phrases are not equals.
So it is necessary to use a regular expression to extract the relevant parts:
public static void printRelevant(String string) {
Pattern numberPattern = Pattern.compile("[0-9]+");
Pattern booleanPattern = Pattern.compile("true|false");
Matcher numberMatcher = numberPattern.matcher(string);
Matcher booleanMatcher = booleanPattern.matcher(string);
if (numberMatcher.find() && booleanMatcher.find()) {
return numberMatcher.group(0) + " " + booleanMatcher.group(0);
}
throw new IllegalArgumentException("String not valid");
}
Step 1: Use String splitting by space to put all elements in an array
Step 2: Find the indexes of the elements you're interested in
Step 3: Parse each element String into the required type (Integer and Boolean)
That should get you started!
If you always have the same "pattern" of the input string, you can simply do:
input = "The data of Branch 1 are correct - true";
// split by space
String [] words = input.split(" ");
// take the values (data) that you need.
System.out.println(words[4] + " " + words[8]);
// also you can cast values to the needed types
Something like this.
Best way, probably, will be to use Regex to take needed data from the input string.

JDBCTemplate queryForMap compare and replace value in extracted lines

Can you help me to understand why my code doesn't work, please?
I am trying to get values from 2 columns from my database and store them in a hashmap where K_PARAM is my key and L_PARAM is my value. Then I would like to compare 2 characters from a line that I am extracting and see if these 2 characters are equals to my key or not. In case they are equals, I replace key with value.
Thanks in advance. This is the code :
if (action.equals("RP")) {
if (marqueCarte = null) {
jdbcTemplate.query(" select K_PARAM, L_PARAM from DLCOA.DLC_ADM_PARAMS where K_CHX_PARAM = '50'", new ResultSetExtractor<Map>(){
#Override
public Map extractData(ResultSet rs) throws SQLException,DataAccessException {
 HashMap<String,String> marqueCarte = new HashMap<String,String>();
while (rs.next()) {
marqueCarte.put(rs.getString("K_PARAM"),rs.getString("L_PARAM"));
if (line.contains("blocE")) {
if (line.substring(line.indexOf("blocE") + 15, line.indexOf("blocE") + 15 + (line.substring(line.indexOf("blocE")+15)).indexOf("#")).equals(rs.getString("K_PARAM"))){
line = line.replace(line.substring(line.indexOf("blocE") + 15, line.indexOf("blocE") + 15 + (line.substring(line.indexOf("blocE")+15)).indexOf("#")),rs.getString("L_PARAM") );
}
}
   }
return marqueCarte;
}
}
}
}
I got a more readable and modifiable solution for your second problem.
(I'm still not sure what's your first one)
Using regex and patterns you can achieve the replacement you want.
Let's assume that you are searching for the text "blocE" followed by 15 characters, followed at the same time by the text contained in rs.getString("K_PARAM") plus an "#"
We can model what you search as a pattern like this
"(blocE)(.{15})(" + key + "#)"
Parenthesis allow us to establish different groups in the regex.
Group 1 - blocE
Group 2 - 15 characters
Group 3 - key + #
Being group 0 the complete matching expression.
Knowing this you can do the replacement applying the following code
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestRegex {
public static void main(String[] args) {
String key = "KEY"; // rs.getString("K_PARAM")
String value = "VALUE"; // rs.getString("L_PARAM")
Pattern pattern = Pattern.compile("(blocE)(.{15})(" + key + "#)");
String input ="helloworldblocE111111111111111KEY#blocE111111111111111KEY";
Matcher m = pattern.matcher(input);
if (m.find()) {
String text2replace = m.group(0);
String replacement = m.group(1) + m.group(2) + value;
System.out.println(input.replaceFirst(text2replace, replacement));
}
}
}
If your pattern changes, you only have to change one line and you do not have to worry about such quantity of indexOf.

Parsing URI query losing values beyond pound sign

When passing a URI query which contains # the function seems to stop iterating the query and returns up to just before the #
Example URI: /test.php?hello=Hello+World ljlksjlkdja(#*!!!!()**!&world=Venus
Will output: Hello World ljlksjlkdja(
Expected Output: Hello World ljlksjlkdja(#*!!!!()**! from Venus
I have tried replacing the pound signs with query.replace("#", "%23") after tokens but the problem persists so I am not sure.
Main function it's based on can be found here: Parse a URI String into Name-Value Collection
Alternately I noticed the author mentioning this would work on arrays, but it only captures the first result from something like ?hello=Hello+World&world[]=Venus&world[]=Mars&world[]=Eartth which outputs an array [world]=>Array([0] => Venus)
private static Map<String, List<String>> splitQuery(String query) throws UnsupportedEncodingException {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
String[] tokens = query.split("\\?", 2);
if (tokens.length == 2) {
query = tokens[1];
final String[] pairs = query.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
query_pairs.get(key).add(value);
}
}
return query_pairs;
}
Your code and the example you provide compiles and runs for me:
System.out.print(splitQuery("asdfasdfadsf?hello=Hello+World"));
System.out.print(splitQuery("asdfasdfadsf?hello=Hello%20World"));
# output: {hello=[Hello World]}{hello=[Hello World]}
One suggestion would be to use split() to find key=value pairs instead of manually splitting based on character indexes.
Even better, I'd consider using a third party library to do this work, as suggested in Parse a URI String into Name-Value Collection.
Updated to address question updates
The # character in a URL introduces a fragment identifier. According to RFC 3986, Section 3.5: Fragment:
A fragment identifier component is indicated by the presence of number sign ("#") character and terminated by the end of the URI.
Thus, it makes sense that query parameter processing ends when the # character has been encountered. In order to accept these kinds of characters in your query parameters, they must be encoded. # is encoded as %23, but this encoding must occur before you actually send the request to the server. Using your example, the following should work as you intend:
/test.php?hello=Hello%2BWorld%20ljlksjlkdja(%23*!!!!()**!&world=Venus
See also Which characters make a URL invalid? for a discussion on valid URL characters.

Replacing comma found in an expression enclosed by one or more double quotes

I have a string like this
" val1 , \"val2 , \" val3, val4\" \" , \" val5,val6 \""
I want to replace comma as in this output
val1 , " val2 - " val3-val4 " " , " val5-val6 "
My trying to solve this problem
public String getResult(String input)
{
String result="";
int counter=0;
for(int i=0; i<input.length(); i++)
{
if(input.charAt(i)=='"')
{
counter++;
result =result+input.charAt(i);
}
else if(input.charAt(i)==',' && counter%2!=0)
{
result=result+'-';
}
else
{
result=result+input.charAt(i);
}
}
return result;
}
but it didn't work
Andreas is right! You need to set the rules as to how the string you want to process will be created unless of course this is data from some obscure data file (or source) and you really don't have a choice in the matter whatsoever and your task is to simply parse it out with a big "good luck" attached to it.
By the look of things, the rules of engagement are: If there is a comma delimiter (,) in any text contained between (within) double-quote characters or nested double-quote characters then change that comma delimiter to a hyphen (-).
Since the double-quotes are acting like parentheses in a way, the first thing you need to do is to make sure the supplied data string is actually playing by the rules and if it isn't then just don't bother with it simply because...it's cheating and not playing by the rules :). To do this we need to ensure that there are even pairs of double-quotes contained within the data string just as there would be with open/close pairs of parentheses and here's one way of doing this:
// Make sure there is an even number of double-quotes...
if (((dataString.length() - dataString.replace("\"", "").length()) & 1) != 0) {
// No...so inform User (via console window) and Exit method.
System.out.println("\u001B[31m*** ERROR! ***\n\u001B[31mDouble-Quote Pairs "
+ "Mismatch In Supplied Data String!\nData String: \u001B[34m" +
dataString + "\n");
return null;
}
Note: The string message above which is sent to console window
contains escaped color codes for changing the displayed colors of text
("\u001B[31m" and "\u001B[34m").
If we have an even number of double-quote characters then we obviously have matched pairs of double-quotes. With this now established we can carry on and retrieve the string data between each pair of quotes and make the necessary modifications to that data so that we can parse it out properly later on. The easiest thing to do here now is to literally remove all those darn spaces from the supplied data string. When we parse this data out we may not want them but hey, if you do then forget about this step:
dataString = dataString.replaceAll(" ", "");
would ultimately change the contents of the dataString variable to:
"val1,\"val2,\"val3,val4\"\",\"val5,val6\"";
To me, It's just easier to work with if you don't have to worry about a lot of different conditions for spacing later on since all we're going to do is parse the final result string anyways.
Now, the simplest way I can think of off hand to quickly pull out data from between quotes or double-quotes within a string for processing is using the String.split() method:
String[] ss = dataString.split("\"");
The string array we've just created (contained within the ss variable) has actually helped us establish which parts of the supplied data string was contained between double-quotes. We just need to realize this and work with the data accordingly. The first step would be to declare a result string variable conveniently named res (to parse out later on) and at the same time we'll initialize it with the first element from our parsed data string, it really doesn't play any part in our processing other than start the make-up of our result string. If the first portion of data within the supplied data string was between double-quotes as in:
"\"val1\" , \"val2 , \" val3, val4\" \" , \" val5,val6 \""
and we split the supplied data string with the String.split() method using the double-quote as a delimiter then our first element of the array created from the split would be a Null String ("") anyways which will really do nothing to our result string we're about to create with the following for loop. Array elements containing a Null String are handled within this for loop as you can see below:
for (int i = 1; i < ss.length; i++) {
if (ss[i].equals("") || ss[i].equals(",")) { res+= ss[i]; }
else { res+= ss[i].replace(",", "-"); }
}
The declaration and initialization of our result string variable (res) and this small for loop will create the string we'll need to properly parse out the supplied data string according to the rules you provided, at least for the most part. You may need to do some tweaking with the conditions contained within the for loop to handle all the rules you still haven't shared with us.
All you need to do now is to parse the result string variable (res) to acquire the data you want and yes, you simply use the String.split() again to do this:
String[] parsedData = res.split(",")
return parsedData;
The parsedData array will contain.... your required parse data to do with as you wish. The entire method would look like this (we'll call it the parseQuotedData() method):
private static String[] parseQuotedData(String dataString) {
// Make sure there is an even number of double-quotes...
if (((dataString.length() - dataString.replace("\"", "").length()) & 1) != 0) {
// No...so inform User (via console window) and Exit method.
System.out.println("\u001B[31m*** ERROR! ***\n\u001B[31mDouble-Quote Pairs "
+ "Mismatch In Supplied Data String!\nData String: \u001B[34m" +
dataString + "\n");
return null;
}
// Remove whitespaces from Data String.
dataString = dataString.replace(" ", "");
// Split (parse) the data string at each double-quote
// into an array named ss ...
String[] ss = dataString.split("\"");
// Declare a result string to return from this method and
// initialize it with the first element from our parsed
// data string.
String res = ss[0];
// Iterate through the remainder of the split data and
// deal with the commas contained between double-quotes.
for (int i = 1; i < ss.length; i++) {
if (ss[i].equals("") || ss[i].equals(",")) { res+= ss[i]; }
else { res+= ss[i].replace(",", "-"); }
}
// Delete the following if you don't want it...
System.out.println("My Result String: \u001B[34m" + res);
// Parse and return data...
String[] parsedData = res.split(",");
return parsedData;
}
You might use this method like this:
String[] myData = parseQuotedData("val1 , \"val2 , \" val3, val4\" \" , \" val5,val6 \"");
System.out.println("\nMy Parsed Data String:\n======================");
for (int i = 0; i < myData.length; i++) {
System.out.println(myData[i]);
}

Resources