I can see heaps of these sorts of questions but looking through them I'm struggling to find the answer and have already spent a couple days on this issue. Could use some direction for deserializing a response I am receiving to pull the required fields into an iterable.
API: https://statsapi.web.nhl.com/api/v1/schedule?teamId=55&startDate=2022-10-01&endDate=2023-04-21
Problem for me is there are multiple levels here and I'm concerned the nested lists might be an issue. Trying to grab lower level objects continues to return a null for me. This is the example json (full output above).
{
"copyright" : "",
...
"metaData" : {
"timeStamp" : "20220723_234058"
},
"wait" : 10,
"dates" : [ {
"date" : "2022-10-12",
...
"games" : [ {
"gamePk" : 2022020009,
...
"status" : {
"abstractGameState" : "Preview",
...
},
"teams" : {
"away" : {
"leagueRecord" : {
"wins" : 0,
...
},
"score" : 0,
"team" : {
"id" : 55,
"name" : "Seattle Kraken",
"link" : "/api/v1/teams/55"
}
},
"home" : {
"leagueRecord" : {
"wins" : 0,
...
},
"score" : 0,
"team" : {
"id" : 24,
"name" : "Anaheim Ducks",
"link" : "/api/v1/teams/24"
}
}
},
"venue" : {
"id" : 5046,
"name" : "Honda Center",
"link" : "/api/v1/venues/5046"
},
"content" : {
"link" : "/api/v1/game/2022020009/content"
}
} ],
"events" : [ ],
"matches" : [ ]
}, ...
I started by just trying to slice it up on my controller for testing but going beyond the 'games' level it just starts returning null for everything. Dates were fairly easy enough to get but the actual team names just resulted in everything being returned as null.
#GetMapping("/test")
#ResponseBody
public ArrayList<String> teamSchedule(#RequestParam int team) throws JsonProcessingException {
String nhlScheduleAPI = "https://statsapi.web.nhl.com/api/v1/schedule?teamId=";
String nhlScheduleRange = "&startDate=2022-10-01&endDate=2023-04-21";
String teamScheduleURL = nhlScheduleAPI + team + nhlScheduleRange;
RestTemplate restTemplate = new RestTemplate();
JsonNode data = restTemplate.getForObject(teamScheduleURL, JsonNode.class);
ArrayList<String> dates = new ArrayList<>();
data.forEach(game -> {
dates.add(data.get("dates").toString());
});
return dates;
I've started to create a PoJo class but a bit overwhelmed by the number of fields and sub-classes being used. I am attempting to rebuild a schedule app that I previously created in Python/Django but struggling to sanitize the data from the api. I'm only needing three items for each of the 82 games.
[<date>, <home_team>, <away_team>]
Is there an easier way to do this? Really appreciate any guidance here.
If you inspect the Json node structure correctly you would access the dates like this:
JsonNode data = restTemplate.getForObject(teamScheduleURL, JsonNode.class);
data = data.get("dates");
ArrayList<String> dates = new ArrayList<>();
data.forEach(d -> {
dates.add(d.get("date").toString());
});
return dates;
For the sake of others who may be in need of learning Json with Java/Spring, or more specifically parsing the NHL API, I am adding my solution below. It likely isn't the best way to achieve a reduced list of games but it works. The problem I was having through this was not having a good understanding of how Java classes map to nested json objects.
SchedulePOjO
#JsonIgnoreProperties
public class SchedulePOjO {
private ArrayList<DatesPOjO> dates;
// Getters and Setters
}
DatesPOjO
#JsonIgnoreProperties
public class DatesPOjO {
private ArrayList<GamesPOjO> games;
public ArrayList<GamesPOjO> getGames() {
return games;
// Getters and Setters
}
GamesPOjO
#JsonIgnoreProperties
public class GamesPOjO {
private String gameDate;
private TeamsPOjO teams;
// Getters and Setters
}
TeamsPOjO
#JsonIgnoreProperties
public class TeamsPOjO {
private AwayPOjO away;
private HomePOjO home;
// Getters and Setters
}
AwayPOjO
#JsonIgnoreProperties
public class AwayPOjO {
private TeamPOjO team;
// Getters and Setters
}
TeamPOjO
#JsonIgnoreProperties
public class TeamPOjO {
private int id;
private String name;
private String link;
// Getters and Setters
}
ScheduleController
#GetMapping("/test")
#ResponseBody
public SchedulePOjO teamSchedule(#RequestParam int team) throws JsonProcessingException {
// construct url
String nhlScheduleAPI = "https://statsapi.web.nhl.com/api/v1/schedule?teamId=";
String nhlScheduleRange = "&startDate=2022-10-01&endDate=2023-04-21";
String teamScheduleURL = nhlScheduleAPI + team + nhlScheduleRange;
// collect data
RestTemplate restTemplate = new RestTemplate();
SchedulePOjO schedulePOjO = restTemplate.getForObject(teamScheduleURL, SchedulePOjO.class);
return schedulePOjO;
Related
I've been making a simulated sports pyramid (sort of like the English football pyramid) in Java with various leagues and a complex system of tiers and leagues and tournaments and promotion/relegation between them. It's pretty complicated and it's a lot of information, so I didn't want to hardcode it, so I've been trying to put all the information defining the structure of the whole system in configuration files instead. Initially, I put that information in a series of csv files that look like this:
3,4,-3,5
3,2,2,1
4,3,2,3,0
6,5,1,2
7,6,5,2,3,0
8,7,1,2,9
I wrote a function that parses it, and that works absolutely fine, but, just looking at it, that file's pretty incomprehensible; I had to write a whole guide so that I could interpret what these files meant (and that's not even considering how awful it would be for anyone else if they took a look at this).
So, I want to put this data in some sort of key:value system instead for it to be more readable. The data itself is defining a whole bunch of different tournaments, leagues, etc., so it'd be ideal if the data could be nested, so I don't think Properties will work for it (although I'd prefer something that wouldn't need external libraries).
I've worked with JSON before with other languages, so I impulsively decided to encode all the data into a config.json file before I knew how I would deserialize it into my application.
My data (in JSON at least), looks like something like this:
{
"tiers" : [
{
"id" : 100,
"name" : "League of Kings",
"scheduleString" : "standard",
"leagues" : [16]
},
{
"id" : 200,
"name" : "The Neck",
"scheduleString" : "standard",
"leagues" : [8]
},
{
"id" : 300,
"name" : "Group Tier",
"scheduleString" : "standard",
"leagues" : [5,5,5,5,5,5]
}
],
"movements" : [
{
"origin" : [100],
"destination" : [200],
"comparator" : "standard",
"ranks" : [15,16],
"priority" : 1,
"group" : 0
},
{
"origin" : [100],
"destination" : [1],
"comparator" : "random",
"ranks" : [1],
"priority" : 2,
"group" : 0
},
{
"origin" : [200],
"destination" : [1],
"comparator" : "standard",
"ranks" : [8],
"priority" : 1,
"group" : 0
},
{
"origin" : [200],
"destination" : [100],
"comparator" : "standard",
"ranks" : [1,2,3],
"priority" : 1,
"group" : 0
}
],
"tournaments" : [
{
"origin" : [[300]],
"destination" : [200],
"comparator" : ["standard"],
"ranks" : [[1]],
"output" : 2,
"winners" : true,
"special" : false,
"priority" : 1,
"group" : 0
}
],
"topTier" : 100,
"maxRounds" : 50,
"multichampThreshold" : 5,
"deadCollectionId" : 1
}
The main problem that I have is that there's so many JSON Java libraries out there, I have no idea which one (if any) will work best for my application here.
I have my own constructors for these objects that I'd like to use, so I don't really want to use the direct JSON -> object mapping tools that I've in some of the libraries out there (edit: this is called data binding I think?).
I'd just like to be able to do something sort of like this:
JSONObject obj = JSON.parseFile("config.json");
ArrayList<Tier> tiers = new ArrayList<Tier>();
for (JSONObject tierObj : obj.getArray("tiers")) {
Tier newTier = new Tier(tierObj.getInt("id"), tierObj.getString("name"));
tiers.add(newTier);
}
Which Java JSON library do you think would be the best for me to use here? Or should I switch back to using some other sort of data encoding (Properties, XML, etc.)? Or should I just write my own JSON parser to do the work instead?
Here are the relevant class definitions:
public final class Tier {
private final int id;
private final String name;
private final League[] leagues;
}
public class TeamCollection {
private final int id;
private final String name;
private final int maxLength;
private ArrayList<Team> teams;
}
public final class League extends TeamCollection implements Comparable<League> {
private final String scheduleString;
private int[][][] schedule;
}
public class MovementHandler {
private final TeamCollection[] origin;
private final TeamCollection[] destination;
private final RankHandler rh;
private final int priority;
private final int group;
private boolean active;
}
public class TournamentHandler {
private final TeamCollection[][] origins;
private final TeamCollection[] destination;
private final RankHandler[] rhs;
private final Tournament tournament;
private final int priority;
private final int group;
private boolean active;
}
public final class Tournament {
private final int id;
private final String name;
private final int numOutputTeams;
private final boolean winnersBracket;
private final boolean special;
private boolean printPlayerMatches;
private ArrayList<Team> inputTeams;
private ArrayList<Team> outputTeams;
private ArrayList<Team> currentTeams;
private int round;
}
public class RankHandler {
private final Comparator<Team> comparator;
private final int[] ranks;
private final boolean random;
private final boolean all;
}
So, just to clarify what exactly I want the configuration file to define, I want the "tiers" section of the configuration file to define a list of Tiers (and the Leagues that are contained within), and I want the "movements" section to define a list of Movement Handlers (and the Rank Handlers that they use), and I want the "tournaments" section to define a list of Tournament Handlers (and the Rank Handlers that they use and the Tournaments that they are associated with; a 1:1 ratio there). And I also have a couple of normal configuration things that I need to set, but I don't think that should be a problem.
I am trying to figure out how to return multiple JSON items. Right now I am able, to return a single JSON like so:
{
"result": {
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
"error": null
}
But I would like to return multiple JSON items, like so:
{
"result": {{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
}
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
},
"error": null
}
I can view the multiple JSON items as string, like below, but I would like to return it as multiple JSON items, but I don't know how:
[LDAPModel(userId=abcde123, telephoneNumber=1-555-5555555), LDAPModel(userId=fghi456, telephoneNumber=1-333-3333333]
I am a complete beginner in Java, and I don't know the syntax or much in Java. But I was given these codes (including the one below) from SpringBoot; I really don't understand what it is doing, and so I have no idea how create an output of list.
Currently, this is what I was given:
public Optional<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
// for (int ii = 0; ii < ldapModelList.size(); ii++) {
// Optional.of(ldapModelList.get(ii));
// ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(ii));
// }
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(0));
}
I tried putting it in a loop (like in the commented out code above), but I don't know how create a list. I tried removing the get(0), but there was a syntax error... There are many things I tried, but it just did not help.
Anyone can help?
Update/Edit: Thank you all for your answers. I posted a follow up question here. If you have a chance, please help me out. Thanks.
First of all I would like to point out that your JSON isn't formatted properly. When you want to represent multiple objects in JSON you should use square brackets and separate each object with a comma:
{
"result": [
{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
],
"error": null
}
The reason your Java code does not work when you try and remove get(0) is because the method public Optional<LDAPModel> getDirectReports(String cdsID) returns an Optional<LDAPModel> type and by removing get(0) your are effectively trying to return an Optional<List<LDAPModel>>. If you want the method to return a list instead of a single object you can change the return type to Optional<List<LDAPModel>> and then safely remove get(0).
public Optional<List<LDAPModel>> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList);
}
The structure looks strange to me. What you have looks like you want result to be an array of objects:
{
"result": [
{ "userId": "abcde123",
"telephoneNumber": "1-555-5555555" }
{ "userId": "fghi456",
"telephoneNumber": "1-333-3333333" }
],
"error": null
}
Given a reasonable JSON library, then the value of the "result" member of the JSON object is a JSON array, from which you can then pick out each element in turn by indexing, and each element is a JSON object with 2 members.
I assume you already managed to get all the list/array of LDAPModel i.e. List ldapModelList
If so, you just need to return this ldapModelList in your getDirectReports method.
public List<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList;
}
Then just use your library to return the json array. I suppose you use jackson.
Just make sure in LDAPModel you have
getters and setters
empty constructor if you add your own constructor having params. But if you don't add any constructor, then no need to add this default empty constructor as java will automatically create it for you.
LDAPModel class is as follows:
public class LDAPModel {
String userId;
String telephoneNumber;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setTelephoneNumber(String telephoneNumber) {
this.telephoneNumber = telephoneNumber;
}
}
For the object to JSON string conversion using Jackson, I assume you already know it or can find out how.
I am using the following query:
NativeSearchQuery nsq = new NativeSearchQueryBuilder()
.withQuery(qb)
.withPageable(PageRequest.of(page, PAGE_SIZE))
.withSort(sb)
.build();
With the following SortBuilder:
SortBuilders.geoDistanceSort("customer.address.geoLocation",
customer.getAddress().getGeoLocation().toGeoPoint())
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS);
Which is producing the desired sort query:
"sort": [
{
"_geo_distance" : {
"customer.address.geoLocation" : [
{
"lat" : 40.4221663,
"lon" : -3.7148336
}
],
"unit" : "km",
"distance_type" : "arc",
"order" : "asc",
"validation_method" : "STRICT",
"ignore_unmapped" : false
}
}
]
Aditionaly each resulting document is returning the distance to to the reference parameter in km:
"sort": [2.4670609224864997]
I need this value as part as my domain but I simply cannot push it into my object. I tried a simple approach as definig it into my domain as what it seems to be a float[] but I keep getting null. I'm using Jackson for (de)serializing.
private float[] sort;
public float[] getSort() {
return this.sort;
}
public void setSort(float[] score) {
this.sort = score;
}
My repository:
public interface ElasticsearchProductRepository
extends ElasticsearchRepository<Product, String> {
Page<Product> search(SearchQuery searchQuery);
}
Product:
#Entity
#Table(name = "product")
#Document(indexName = "product", createIndex = true, type = "_doc")
#Setting(settingPath = "elasticsearch/product-index.json")
#DynamicTemplates(mappingPath = "elasticsearch/product-dynamic-templates.json")
public class Product {
...
#org.springframework.data.annotation.Id
#javax.persistence.Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(ResponseView.class)
private long id;
#Field(analyzer = "autocomplete", type = FieldType.Text, searchAnalyzer="standard")
#JsonView(CreationRequestView.class)
private String productName;
private float[] sort;
...
public float[] getSort() {
return this.sort;
}
public void setSort(float[] score) {
this.sort = score;
}
}
I added the following scripted field:
"script_fields": {
"distance": {
"script": {
"lang": "painless",
"source": "doc['sort']",
"params": {
}
}
}
}
But it is not finding the sort field.
"reason": "No field found for [sort] in mapping with types []"
It seems that doc references doc._source in reality... how can I reference this great piece of data from an Elastic script?
Currently in Spring Data Elasticsearch the returned entity is populated from the values returned in the _source field. The returned sort is not part of that, but a part of the additional information return for each search hit.
For the next release (4.0) we are currently in the process of rewriting how returned hits are handled. The next version will have a SearchHit<T> class where T is the entity domain class, and the SearchHit object contains this entity along with other information like score, highlight, scripted fields, sort etc.
It is planned that the repository methods then can for example return Lists or Pages of these SearchHit objects.
But currently, the sort from the query will lead to a sorted return, but you cannot retrieve the sort values themselves - which in your case is bad, because they are calculated dynamically.
Edit:
You can retrieve the value by defining a property for the geo_distance in your entity and annotate this with the #ScriptedField annotation, and then provide the script in your native query. An example can be found in the at tests for Spring Data Elasticsearch. This example works with the ElaticsearchTemplate, but it should make no difference passing this query into a repository search method.
Edit 25.12.2019:
The current master branch, which will become Spring Data Elasticsearch 4.0, has this implemented. The found documents are returned in a SearchHit object that also contains the sortValues.
my first post here. I'm working on a API and i found a trouble that i don´t know how to solve it.
I´m trying to get the remaining stock of all products in my database. I´m using Spring boot and MongoDB with the Spring Data dependency.
Here is my code:
#GetMapping("/remaining-stock")
public ResponseEntity<List<Pair<String, Integer>>> showAllStock() throws EmptyDepositException{
List<Pair<String, Integer>> allStock;
try {
allStock = depServ.showAllStock();
}catch(EmptyDepositException ex) {
allStock = null;
}
return ResponseEntity.ok(allStock);
}
When i do that GET request, this is the JSON i get:
[
{
"first": "Water",
"second": 5
},
{
"first": "Milk",
"second": 40
}
]
The values are OK but i want to rename the variables names with a better name like this:
[
{
"Product name": "Water",
"Remaining stock": 5
},
{
"Product name": "Milk",
"Remaining stock": 40
}
]
There is a way to do that?
Sorry for my english, i'm from Argentina so maybe something is not clear. I hope you can help me.
Thank you all in advance.
You can use #JsonProperty annotation which indicates that the field name is used as the property name without any modifications, but it can be specified to non-empty value to specify different name. Property name refers to name used externally, as the field name in JSON objects.
public class Product implements Serializable {
#JsonProperty("Product name")
private String first;
#JsonProperty("Remaining stock")
private long second;
// implement methods for getters and setters
}
With Pair Class, you can't do that. I suggest you to create a DTO class like below :
public class CustomPair {
#JsonProperty("Product name")
private String first;
#JsonProperty("Remaining stock")
private String second;
// standard getters and setters
}
Then your controller will be like this :
#GetMapping("/remaining-stock")
public ResponseEntity<List<CustomPair>> showAllStock() throws EmptyDepositException{
List<CustomPair> allStock;
try {
allStock = depServ.showAllStock(); // depServ.showAllStock(); should return List<CustomPair>
}catch(EmptyDepositException ex) {
allStock = null;
}
return ResponseEntity.ok(allStock);
}
My resource is
#GET
#Path("/items")
public MyCollection<Items> getItems()throws Exception{
//Code to return MyCollection<items>
}
My Item class is
#XmlRootElement
public class Item{
private int id;
private String name;
//Have getters and Setters.
}
And My collection class is Generic as below.
public class MyCollection<T> extends MyBaseCollection{
private java.util.Collection<T> items;
private int count;
}
When i try to generate doc using enunciate. The sample Json has only the item and count and the fields of Item class is not getting reflected.
My sample Json generated is
{
"items" : [ {
}, {
}, {
}, {
}, {
}, {
}, {
}, {
} ],
"count" : ...,
}
How to get id,name inside the Item in the generated sample Json?
Thanks.
This is a limitation that i have run into as well, there is no way to specify #TypeHint on a nested object. To support documentation, consider creating a custom collection that defines "items" as a collection of specific class instead of generic.
If you have an idea of how you would want this to work (using the generic type) I suggest submitting enhancement request to Enunciate team.
I have a similar problem where I am returning a Map and I can't #TypeHint this.