Spring Expression Language (SpEL) for json access with list - spring

I'm trying to understand why this doesn't work?
context.addPropertyAccessor(new JsonPropertyAccessor());
Object j = mapper.readTree(
"{\"foo\": {\"bar\": [ { \"fizz\": 5, \"buzz\": 6, \"doo\": 9 }, {\"fizz\": 7}, {\"fizz\": 8} ] } })");
System.out.println(evaluate(j, "foo.bar.![doo == 9]"));
It always prints:
[false, false, false]
I need to check if any of the doo contains a 9.

If you don't need use SPEL necessarily you can do it easily with Jayway JsonPath
Include dependency:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
Then use the JsonPath and query:
Srgin json = "{\"foo\": {\"bar\": [ { \"fizz\": 5, \"buzz\": 6, \"doo\": 9 },
{\"fizz\": 7}, {\"fizz\": 8, \"doo\": 6} ] } })";
JSONArray array = JsonPath.read(json, "foo.bar[?(#.doo==9)]");
// now array is a List of size 1, since second "doo" is not equal 9
// using array.get(0) you can get first object as a HashMap<String, Object>
By the link mentioned above you can find more details about JsonPath queries.
If you still need to integrate JsonPath with SPEL you can add parsing function to the evaluation context. Fortunately you already use spring-integration which has JsonPathUtils class with evaluate(String, String) method.
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
Method method = BeanUtils.resolveSignature("evaluate", JsonPathUtils.class);
evaluationContext.registerFunction("jsonPath", method);
String json = "{\"foo\": {\"bar\": [ { \"fizz\": 5, \"buzz\": 6, \"doo\": 9 }, " +
"{\"fizz\": 7}, {\"fizz\": 8, \"doo\": 7} ] } })";
evaluationContext.setVariable("foo", json);
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("#jsonPath(#foo, 'foo.bar[?(#.doo>6)]')");
Object result = expression.getValue(evaluationContext);
//now result contains still the same JSONArray
And finally I figured out the reason of your issue. First of all: guess what returns the expression foo.bar.doo? It's not a String or Integer value. It returns an instance of ToStringFriendlyJsonNode. So doo==9 will always give you false since they can't be equal. Moreover you can't use > or < because it's not allowed to compare Object and Integer. Therefore you need to cast to suitable type before comparison.
expression.getValue("foo.bar.![doo!=null and new Integer(doo.toString()) == 9]");
Also we need to check if doo exists that's why we need to check for null. ToStringFriendlyJsonNode can provide only string value. That's why we have to use ToString(). As you see native SPEL is inconvenient and too verbose because it's not supposed to be used for complex Json queries.

Related

JSON is object instead of array, if array_diff returns assoc array on Collection->toArray()

My issue is in my json I am expecting an array, but am getting an object.
Details:
I have an array of numbers:
$numbers = [1];
I select from relationship, the "drawn numbers":
$drawnNumbers = Ball::whereIn('number', $numbers)->where('game_id', $card->game->id)->get()->map(function($ball) {
return $ball->number;
})->toArray();
I do a ->toArray() here. I want to find the numbers in $numbers that do not occur in $drawnNumbers. I do so like this:
$numbersNotYetDrawn = array_diff($numbers, $drawnNumbers);
My method then return $numbersNotYetDrawn (my headers accept is application/json).
So now the issue. When $drawnNumbers is an empty array, then the printed json is a regular array like this:
[
1
]
However if the relationship returns $drawnNumbers to be an array with numbers, then json is printed as an object:
{
"0" => 1
}
Does anyone know why this is? Anyway to ensure that json is array?
Edit:
Here is my actual data:
$drawnNumbers = Ball::whereIn('number', $numbers)->where('game_id', $card->game->id)->get()->map(function($ball) {
return $ball->number;
})->toArray();
$undrawnNumbers = array_diff($numbers, $drawnNumbers);
// $undrawnNumbers = array_values(array_diff($numbers, $drawnNumbers)); // temp fix
Replace
$numbersNotYetDrawn = array_diff($numbers, $drawnNumbers);
with
$numbersNotYetDrawn = array_values(array_diff($numbers, $drawnNumbers));
to make sure element keys are reset and array is treated as a simple list and serialized to a JSON list - instead of being treated as an associative array and serialized to a JSON object.
I recently had this same problem and wondered the same thing.
I solved it by adding "array_values", but I was wondering how to reproduce it.
I found it that it is reproduced when array_diff removes an element from the array that isn't the last element. So:
>>> $x
=> [
1,
2,
3,
4,
5,
]
>>> array_diff($x, [5]);
=> [
1,
2,
3,
4,
]
>>> array_diff($x, [1]);
=> [
1 => 2,
2 => 3,
3 => 4,
4 => 5,
]

Laravel eloquent gives array of numbers, instead of integers

I'm trying to return id's as a string and pass them via api (using for select later)
Using Laravel resouces:
public function toArray($request)
{
return [
'speciality' => $this->specialities()->pluck('speciality_id')
];
}
and it returns an array of numbers, like:
[1, 3, 5, 7]
How can I convert them within eloquent query and return like a string?
["1", "3", "5", "7"]
You could loop through the array, cast it to string and add to new array as it is only required for this specific case.
$a = [1, 3, 5, 7];
$b = array();
foreach($a as $as)
$b[] = (string)$as;
return $b;
Or better use array_map() -
$a = array_map(function($value){
return (string) $value;
}, $a);
It's a bit awful, but if have no choice then
cast it to string
protected $casts=[
'speciality_id'=>'string'
];

Filter Enums based on List in LINQ

I Have a Enum
public enum ProcessStatus: byte
{
NotStarted = 0,
PreCheckStarted= 1,
PreCheckCompleted= 2,
Processing= 3,
Failed= 4,
Completed= 5,
Closed= 6
}
in Table we have entries like 0,3,5,6
we need list of Enums based on some criteria and criteria is List which contains 0,1,2
i am able to get all Enums as List Like
Enum.GetValues(typeof(ProcessStatus)).OfType<ProcessStatus>()
and have
List<byte> processListIDs
which contains IDs
i want
IEnumerable<ProcessStatus> filtered based on ids in processListIDs using LINQ.
Thanks in Advance
You can use Intersect with better performance:
var enumList = Enum.GetValues(typeof (ProcessStatus))
.OfType<ProcessStatus>().Cast<byte>();
var result = enumList.Intersect(processListIDs)
.Cast<ProcessStatus>();
var res =
processStatusCollection.Where(item => processListIDs.Contains((int)item));
You could use Enum.TryParse<TEnum>:
List<byte> processListIDs = new List<byte>() { 0, 3, 5, 6 };
ProcessStatus ps = ProcessStatus.NotStarted;
IEnumerable<ProcessStatus> status = processListIDs
.Where(p => Enum.TryParse<ProcessStatus>(p.ToString(), out ps))
.Select(p => ps);
Try this,
var p = new List<byte>() { 1, 2, 3, 4, 6 };
IEnumerable<ProcessStatus> result = p.Select(o => (ProcessStatus)Enum.Parse(typeof(ProcessStatus), o.ToString()));
/// do something with result

Linq To Entities Get 2nd Last entry In List

I have the following code which returns a list of Objects.
var listOfLogins = _logService.GetLogEventsByItemID(137).ToList();
I would like to get the 2nd last object in this list.
Does anyone know how to do this using Linq to Entities?
Thanks.
var secondlast = _logService.GetLogEventsByItemID(137)
.Reverse()
.Skip(1)
.Take(1)
.FirstOrDefault();
Update
#Dherik makes a good point in his comment that .Reverse is not actually supported in LINQ to Entities and will result in the query being evaluated at the point of calling reverse, rather than at the point of calling .FirstOrDefault. See here for all (not) supported methods.
The alternative (LINQ to Entities friendly) solution requires that you have a suitable field to order by (which must be the case anyway otherwise "second last" has no relevance):
var secondlast = _logService.GetLogEventsByItemID(137)
.OrderByDescending(e => e.EventDate /* could be any db field */)
.Skip(1)
.Take(1)
.FirstOrDefault();
int[] items = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int item = items.Skip(items.Count() - 2).Take(1).Single();
//will return 9
like this?

Using Linq to create crosstab results [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is it possible to Pivot data using LINQ?
I'm wondering if its at all possible to create crosstab style results with Linq.
I have some data that looks like the following:
var list = new[]
{
new {GroupId = 1, Country = "UK", Value = 10},
new {GroupId = 1, Country = "FR", Value = 12},
new {GroupId = 1, Country = "US", Value = 18},
new {GroupId = 2, Country = "UK", Value = 54},
new {GroupId = 2, Country = "FR", Value = 55},
new {GroupId = 2, Country = "UK", Value = 56}
};
and I'm trying to output to a repeater control something like the following:
GroupId.....UK.....FR.....US
1...........10.....12.....18
2...........54.....55.....56
Its the dynamic columns that are causing my problems. Any solutions to this?
You need a runtimy class to hold these runtimy results. How about xml?
XElement result = new XElement("result",
list.GroupBy(i => i.GroupId)
.Select(g =>
new XElement("Group", new XAttribute("GroupID", g.Key),
g.Select(i => new XAttribute(i.Country, i.Value))
)
)
);
Are you expecting multiple records per result cell? If so there would need to be some Summing (and more grouping) in there.
(this answer is proof of concept, not final result. There's several issues to address, such as: ordering of columns, missing cells, and so on).
After doing a quick search you might want to look at the ModuleBuilder, TypeBuilder, and FieldBuilder classes in System.Reflection.Emit. They allow you to create a class dynamically at runtime. Outside of that you would need to do grouping on your objects and then do something with the hierarchical results you get from LINQ. I am not sure of a way to dynamically create anonymous type fields at runtime, and that sounds like what would need to happen.
You could try using the dynamic linq library provided by MS. They have a number of overloads to extensions methods that take strings as arguments. They also have an expression parser that takes a string an emits a lambda expression. You should be able to create a dynamic select using them.
A word of warning though, you end up with a non-generic IQueryable rather than a generic IQueryable so you are a little bit limited on what you can do with the result, and you give up a bit of type safety, but that may be OK in your application...
The link for the dynamic linq stuff is
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
There is a link where you can download the source code the the dynamic library, plus some nice illustrations of how you can use it.
var labResults = from lab in CoreLabResults
where lab.Patient == 8
group lab by new { lab.Patient, lab.TestNo, lab.CollectedDate }
into labtests
select new
{
labtests.Key.Patient,
labtests.Key.TestNo,
labtests.Key.CollectedDate,
MCHC = labtests.Where(lab => lab.TestVar == "MCHC").FirstOrDefault().Result,
LYABS = labtests.Where(lab => lab.TestVar == "LYABS").FirstOrDefault().Result,
TotalTests = labtests.Count()
}

Resources