I'm trying to do a convertion from String to Decimal128 in a #Query to perform a paging operation using the following snippet:
public interface ExpenseCollectionRepository extends ReactiveMongoRepository<ExpenseCollection, String> {
Mono<Boolean> existsByDescriptionAndDueDate(String description, LocalDate dueDate);
#Query("{'$or':" +
" [" +
"{'description': '?0'}," +
"{'value': '?#{#search.matches(\"-?\\\\d+(\\\\.\\\\d+)?\") ? T(org.bson.types.Decimal128).parse(#search) : T(org.bson.types.Decimal128).parse(\"0.0\")}}'}," +
"{'paymentDate': '?0'}," +
"{'dueDate': '?0'}," +
"{'type': '?0'}," +
"{'payed': '?0'}" +
"]" +
"}")
Flux<ExpenseCollection> findPage(#Param("search") String search, Pageable pageable);
}
What an I missing? Do I using Spel wrongly?
Github: https://github.com/SergioLuigi/expense-control/blob/bug/stack-over-flow-question/src/main/java/br/com/sergioluigi/expensecontroljava/expense/infrastructure/database/repository/ExpenseCollectionRepository.java
Related
I have a string object representing a json object returning for a network task. I need to convert it into a Map (or HashMap). I've been using gson, but it has been unsuccessful. Here is the json string (please excuse indentation, for I had to manually add newline spaces):
{
"plans":{
"Ankle Recovery":{
"StartDate":"09/24/2018",
"Progress":0.6666666666666666,
"Tasks":[
{
"date":"10/16/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":true,
"requirementType":"steps"},
{
"date":"10/17/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":true,
"requirementType":"steps"
},
{
"date":"10/18/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":false,
"requirementType":"steps"
}
],
"Username":"email#site.com",
"Doctor":"Mike Michaels",
"EndDate":"12/24/2018"}},
"status":true
}
This is the code I've been using to make the transformation:
private Map<String, String> plans;
plans = new Gson().fromJson(result, new TypeToken<Map<String, String>>() {}.getType());
Neither nor has worked. I've tried some different solutions across Stack Overflow, but none yield success to this point.
I'm also getting an exception thrown that I don't quite understand:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 11
(Column 11 is just before the first quote in "AnkleRecovery")
I'd like to use simple gson to make this work if possible. But I'm open to alternative solutions.
The JSON you posted is not valid, line 3:
"Ankle Recovery" : {
// / \
// this is what you are missing
This tool will help you verify the JSON structure and format it as well: https://jsonlint.com/
Now to the actual problem. Your JSON has a following structure:
{
"plans": Object,
"status": Boolean,
}
Neither of these are strings ( object != string, boolean != string ).
Such a structure can not be mapped to Map<String, String> as this requires the value to be a string.
You will need to create multiple POJOs to define your structure and then map to these, e.g.:
class Project {
public Map<String,Plan> plans;
public Boolean status;
}
class Plan {
public String StartDate;
public Double Progress;
public List<Task> tasks;
...
}
class Task {
...
}
Disclaimer...
I would always investigate using one or more POJOs which can be used to represent the data structure if at all possible.
Without more information, it's impossible to know if keys like Ankle Recovery are stable or not, or if they might change.
"A" possible solution
Generally, JSON is in the form of key/value pairs, where the value might be another JSON object, array or list of other values, so you "could" process the structure directly, for example...
String text = "{\n"
+ " \"plans\":{\n"
+ " \"Ankle Recovery\":{\n"
+ " \"StartDate\":\"09/24/2018\",\n"
+ " \"Progress\":0.6666666666666666,\n"
+ " \"Tasks\":[\n"
+ " {\n"
+ " \"date\":\"10/16/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":true,\n"
+ " \"requirementType\":\"steps\"\n"
+ " },\n"
+ " {\n"
+ " \"date\":\"10/17/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":true,\n"
+ " \"requirementType\":\"steps\"\n"
+ " },\n"
+ " {\n"
+ " \"date\":\"10/18/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":false,\n"
+ " \"requirementType\":\"steps\"\n"
+ " }\n"
+ " ],\n"
+ " \"Username\":\"email#site.com\",\n"
+ " \"Doctor\":\"Mike Michaels\",\n"
+ " \"EndDate\":\"12/24/2018\"\n"
+ " }\n"
+ " },\n"
+ " \"status\":true\n"
+ "}";
Gson gson = new Gson();
Map<String, Object> fromJson = gson.fromJson(text, Map.class);
Map<String, Object> plans = (Map<String, Object>) fromJson.get("plans");
Map<String, Object> recovery = (Map<String, Object>) plans.get("Ankle Recovery");
List<Map<String, Object>> tasks = (List<Map<String, Object>>) recovery.get("Tasks");
for (Map<String, Object> taks : tasks) {
for (Map.Entry<String, Object> entry : taks.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
Now, this would get you the output of ...
date = 10/16/2018
amount = 200.0
task = ice ankle for 30 min
completed = true
requirementType = steps
date = 10/17/2018
amount = 200.0
task = ice ankle for 30 min
completed = true
requirementType = steps
date = 10/18/2018
amount = 200.0
task = ice ankle for 30 min
completed = false
requirementType = steps
Having said all that, your own parsing might be a lot more involved, have to inspect if certain keys exist or not and taking appropriate action as required
I'm trying to filter a table called Measure through its field customerId. This is what the beginning of the controller for the path looks like:
#RequestMapping(method = GET, path = "/nodes/{id}/ports/{portid}/measures")
#ResponseBody
public ResponseEntity<?> getPortMeasures(#PathVariable long id, #PathVariable long portid,
#RequestParam Optional<Long> from,
#RequestParam Optional<String> order,
#RequestParam Optional<String> countername,
#RequestParam Optional<Long> to) {
Followed by the method that calls the query undernath
if (order.isPresent() && order.get().equals("asc")) {
return ResponseRestBuilder.createSuccessResponse(
measureRepository.
searchAsc
(networkElementList.get(0).ip, portList.get(0).rack, portList.get(0).frame, portList.get(0).slot,
portList.get(0).portSerial, countername.get(), from.orElse(0L), to.orElse(99999999999999999L)));
}
else{
return ResponseRestBuilder.createSuccessResponse(
measureRepository.
searchDesc
(networkElementList.get(0).ip, portList.get(0).rack, portList.get(0).frame, portList.get(0).slot,
portList.get(0).portSerial, countername.get(), from.orElse(0L), to.orElse(99999999999999999L)));
}
This is what the queries look like:
#Query("SELECT mes FROM Measure mes WHERE " +
"mes.nodeIp = (:nodeIp) AND " +
"mes.rack = (:rack) AND " +
"mes.frame = (:frame) AND " +
"mes.slot = (:slot) AND " +
"mes.portSerial = (:portSerial) AND " +
"lower(mes.counterName) LIKE concat('%', lower(:countername), '%') AND"+
"mes.timestamp > (:timestamp1) AND " +
"mes.timestamp < (:timestamp2) "+
"ORDER BY mes.timestamp DESC")
List<Measure> searchDesc(#Param("nodeIp") String nodeIp, #Param("rack") String rack, #Param("frame") String frame,
#Param("slot") String slot, #Param("portSerial") String portSerial, #Param("countername") String countername,
#Param("timestamp1") Long timestamp1, #Param("timestamp2") Long timestamp2);
#Query("SELECT mes FROM Measure mes WHERE " +
"mes.nodeIp = :nodeIp AND " +
"mes.rack = :rack AND " +
"mes.frame = :frame AND " +
"mes.slot = :slot AND " +
"mes.portSerial = :portSerial AND " +
"lower(mes.counterName) LIKE concat('%', lower(:countername), '%') AND " +
"mes.timestamp > :timestamp1 AND " +
"mes.timestamp < :timestamp2 "+
"ORDER BY mes.timestamp ASC")
List<Measure> searchAsc(#Param("nodeIp") String nodeIp, #Param("rack") String rack, #Param("frame") String frame,
#Param("slot") String slot, #Param("portSerial") String portSerial, #Param("countername") String countername,
#Param("timestamp1") Long timestamp1, #Param("timestamp2") Long timestamp2);
It's not filtering anything because the controller replies with 0 rows. I'm 100% confident there are actual rows because I've checked with other rest calls. What am I doing wrong?
EDIT: debug
Most probably the issue is with the timestamp field ,it seems from the code that you are passing a long, but actually it's waiting for a timestamp literal , timestamp literal in jpa is in the format {ts '2009-11-05 12-45-52.325'} ... just to check , try removing the timestamp from the query then put it again and provide the literals manually ... if it works, you then need to find a way to parse the passed long to the corresponding literal
The problem with it was the field portList.get(0).rack being "null". Appartently this made the whole query not work.
I have a search method in the database that brings me the following result:
As you see that inside the Object [4] comes another array containing People, Pessoas, and PessoasEnderecos and more objects that have relantionship with Pessoas.
I would like it to return only this way and not inside another array as it is happening now, it should look like this:
elementData=Object[10](id=144)
[0]=Pessoas(id=166)
[1]=PessoasEnderecos(id=167)
...
i have this method:
#RequestMapping(method = RequestMethod.GET, value = "/pessoas")
public ResponseEntity<Collection<Pessoas>> buscarPessoas(HttpServletRequest request) throws Exception {
String idEntidadeCrypt = request.getHeader("DataBase");
Long idEntidade = Long.parseLong(Crypto.decode(idEntidadeCrypt));
Collection<Pessoas> pessoasBuscados = pessoasService.buscarFiltro(idEntidade);
return new ResponseEntity<>(pessoasBuscados, HttpStatus.OK);
}
and :
#Repository
public interface PessoasRepository extends JpaRepository<Pessoas, Integer> {
#Query( value="select pes, pEnd, pFis, pJur "
+ "from "
+ "Pessoas pes, "
+ "PessoasEnderecos pEnd,"
+ "PessoasFisicas pFis,"
+ "PessoasJuridicas pJur"
+ " where "
+ "pes.entidade.idEntidade = pEnd.entidade.idEntidade "
+ "and pes.idPessoa = pEnd.pessoa.idPessoa "
+ "and pes.entidade.idEntidade = pFis.entidade.idEntidade "
+ "and pes.idPessoa = pFis.pessoa.idPessoa "
+ "and pes.entidade.idEntidade = pJur.entidade.idEntidade "
+ "and pes.idPessoa = pJur.pessoa.idPessoa "
+ "and pes.entidade.idEntidade = :parametroId " )
public Collection<Pessoas> encontrar(#Param("parametroId") Long usuarioEntidade);
how can i solve that ?
ArrayList<> does not work with magic, it has an actual implementation. elementData is the array where it stores its elements. As you can see (that is a link in the previous sentence), it is a private member of the class, so you do not interact with it, but you see it in a debugger because it exists.
If you want to return an array instead of a List, you can always use toArray() which all Lists implement.
My class having field is List<>, I went output tostring method
private String subjectCode;
private Long clientSn;
private Long ruleId;
private String beginDate;
private List<LessonPreferenceItemDTO> classPlans;
#Override
public String toString() {
return "LessonPreferenceSaveReqDTO{" +
"subjectCode='" + subjectCode + '\'' +
", clientSn=" + clientSn +
", ruleId=" + ruleId +
", beginDate='" + beginDate + '\'' +
", classPlans=" + classPlans +
'}';
}
But, classPlans outout is java entity,don't LessonPreferenceItemDTO field info,So,How was output classPlans in this toString()
You have to override public String toString() for LessonPreferenceItemDTO as well. You may give Apache Commons
ToStringBuilder.reflectionToString a try if you like to output every property.
The class ArrayList for instance will render itself by surrounding its elements with { and } and separating the elements contained by , calling toString() of each contained element.
If you don't like the resulting format of List.toString() you have to serialize the list by yourself while iterating over each element.
You have to override toString() method in LessonPreferenceItemDTO class but that's not all after that you need to call toString() on each element of this list.
You can use Java 8 stream API for that
classPlans.stream()
.map(LessonPreferenceItemDTO::toString)
.collect(Collectors.joining(","));
#Override
public String toString() {
return "LessonPreferenceSaveReqDTO{" +
"subjectCode='" + subjectCode + '\'' +
", clientSn=" + clientSn +
", ruleId=" + ruleId +
", beginDate='" + beginDate + '\'' +
", classPlans=" + classPlans.stream().map(LessonPreferenceItemDTO::toString).collect(Collectors.joining(",")) +
'}';
}
I success output wants,use java 8 stream,very thanks#Harmlezz#Neeraj Jain
I have a string, let's call it output, that's equals the following:
ltm data-group internal str_testclass {
records {
baz {
data "value 1"
}
foobar {
data "value 2"
}
topaz {}
}
type string
}
And I'm trying to extract the substring between the quotes for a given "record" name. So given foobar I want to extract value 2. The substring I want to extract will always come in the form I have prescribed above, after the "record" name, a whitespace, an open bracket, a new line, whitespace, the string data, and then the substring I want to capture is between the quotes from there. The one exception is when there is no value, which will always happen like I have prescribed above with topaz, in which case after the "record" name there will just be an open and closed bracket and I'd just like to get an empty string for this. How could I write a line of Java to capture this? So far I have ......
String myValue = output.replaceAll("(?:foobar\\s{\n\\s*data "([^\"]*)|()})","$1 $2");
But I'm not sure where to go from here.
Let's start extracting "records" structure with following regex ltm\s+data-group\s+internal\s+str_testclass\s*\{\s*records\s*\{\s*(?<records>([^\s}]+\s*\{\s*(data\s*"[^"]*")?\s*\}\s*)*)\}\s*type\s*string\s*\}
Then from "records" group, just find for sucessive match against [^\s}]+\s*\{\s*(?:data\s*"(?<data>[^"]*)")?\s*\}\s*. The "data" group contains what's you're looking for and will be null in "topaz" case.
Java strings:
"ltm\\s+data-group\\s+internal\\s+str_testclass\\s*\\{\\s*records\\s*\\{\\s*(?<records>([^\\s}]+\\s*\\{\\s*(data\\s*\"[^\"]*\")?\\s*\\}\\s*)*)\\}\\s*type\\s*string\\s*\\}"
"[^\\s}]+\\s*\\{\\s*(?:data\\s*\"(?<data>[^\"]*)\")?\\s*\\}\\s*"
Demo:
String input =
"ltm data-group internal str_testclass {\n" +
" records {\n" +
" baz {\n" +
" data \"value 1\"\n" +
" }\n" +
" foobar {\n" +
" data \"value 2\"\n" +
" }\n" +
" topaz {}\n" +
" empty { data \"\"}\n" +
" }\n" +
" type string\n" +
"}";
Pattern language = Pattern.compile("ltm\\s+data-group\\s+internal\\s+str_testclass\\s*\\{\\s*records\\s*\\{\\s*(?<records>([^\\s}]+\\s*\\{\\s*(data\\s*\"[^\"]*\")?\\s*\\}\\s*)*)\\}\\s*type\\s*string\\s*\\}");
Pattern record = Pattern.compile("(?<name>[^\\s}]+)\\s*\\{\\s*(?:data\\s*\"(?<data>[^\"]*)\")?\\s*\\}\\s*");
Matcher lgMatcher = language.matcher(input);
if (lgMatcher.matches()) {
String records = lgMatcher.group();
Matcher rdMatcher = record.matcher(records);
while (rdMatcher.find()) {
System.out.printf("%s:%s%n", rdMatcher.group("name"), rdMatcher.group("data"));
}
} else {
System.err.println("Language not recognized");
}
Output:
baz:value 1
foobar:value 2
topaz:null
empty:
Alernatives: As your parsing a custom language, you can give a try to write an ANTLR grammar or create Groovy DSL.
Your regex shouldn't even compile, because you are not escaping the " inside your regex String, so it is ending your String at the first " inside your regex.
Instead, try this regex:
String regex = key + "\\s\\{\\s*\\n\\s*data\\s*\"([^\"]*)\"";
You can check out how it works here on regex101.
Try something like this getRecord() method where key is the record 'name' you're searching for, e.g. foobar, and the input is the string you want to search through.
public static void main(String[] args) {
String input = "ltm data-group internal str_testclass { \n" +
" records { \n" +
" baz { \n" +
" data \"value 1\" \n" +
" } \n" +
" foobar { \n" +
" data \"value 2\" \n" +
" }\n" +
" topaz {}\n" +
" } \n" +
" type string \n" +
"}";
String bazValue = getRecord("baz", input);
String foobarValue = getRecord("foobar", input);
String topazValue = getRecord("topaz", input);
System.out.println("Record data value for 'baz' is '" + bazValue + "'");
System.out.println("Record data value for 'foobar' is '" + foobarValue + "'");
System.out.println("Record data value for 'topaz' is '" + topazValue + "'");
}
private static String getRecord(String key, String input) {
String regex = key + "\\s\\{\\s*\\n\\s*data\\s*\"([^\"]*)\"";
final Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
//if we find a record with data return it
return matcher.group(1);
} else {
//else see if the key exists with empty {}
final Pattern keyPattern = Pattern.compile(key);
Matcher keyMatcher = keyPattern.matcher(input);
if (keyMatcher.find()) {
//return empty string if key exists with empty {}
return "";
} else {
//else handle error, throw exception, etc.
System.err.println("Record not found for key: " + key);
throw new RuntimeException("Record not found for key: " + key);
}
}
}
Output:
Record data value for 'baz' is 'value 1'
Record data value for 'foobar' is 'value 2'
Record data value for 'topaz' is ''
You could try
(?:foobar\s{\s*data "(.*)")
I think the replaceAll() isn't necessary here. Would something like this work:
String var1 = "foobar";
String regex = '(?:' + var1 + '\s{\n\s*data "([^"]*)")';
You can then use this as your regex to pass into your pattern and matcher to find the substring.
You can simple transform this into a function so that you can pass variables into it for your search string:
public static void SearchString(String str)
{
String regex = '(?:' + str + '\s{\n\s*data "([^"]*)")';
}