I have learned how to use a rest template to access a JSON array like this:
[
{
"ID": "0ae6496f-bb0b-4ebd-a094-ca766e82f3e7",
"Confirmed": 0,
}
{
"ID": "e010ced5-c7cb-4090-a7ed-206f4c482a5b",
"Confirmed": 0,
}
]
I accessed the Confirmed for example with
public Model[] getModel() {
ResponseEntity<Model[]> response = restTemplate.getForEntity(apiUrl, Model[].class);
return response.getBody();
}
but now I have to access data in another Json from another API. The data looks like this
{
"prefixes": [
{
"region": "ap-northeast-2",
"service": "AMAZON",
},
{
"region": "eu-west-3",
"service": "AMAZON",
}
]
}
How can I access region or service inside, and what would be the proper name for this?
The first is a JSON array, the second a JSON object?
The first API is simply
https://example.com
Whereas the second is
https://example.com/data.json.
You have to create the POJO for return type:
List<RegionServiceObject> items;
Where RegionServiceObject looks:
public class RegionServiceObject {
private String region;
private String service;
// constructors, getters/setters, toString()....
}
The way of deseariliseing to object is similar to which you already wrote:
RegionServiceObject[] items = restTemplate.getForObject(url, RegionServiceObject[].class);
and access for specific item will be as usual for specific item:
for (RegionServiceObject item : items) {
item.getRegion();
item.getService();
// use them here
}
Related
I have a data JSON object which has several dynamic fields, because of which I cannot define the schema in Graphql instead I defined it as Json using scalar. My requirement is to filter the fields with the data json object. One sample use case is shown below
query.graphqls
scalar Locale
scalar JSON
type Query {
details: Details!
}
type Details {
locale: Locale!
name: String!
data: JSON!
}
Details.java
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Details {
private Locale locale;
private String name;
private List<?> data;
}
ScalarTypeConfig.java
#Configuration
public class ScalarTypeConfig {
#Bean
public GraphQLScalarType locale() {
return ExtendedScalars.Locale;
}
#Bean
public GraphQLScalarType json() {
return ExtendedScalars.Json;
}
}
SampleController.java
#Component
public class SampleController implements GraphQLQueryResolver {
public Details getDetails() {
Details details = new Details();
details.setLocale(Locale.ENGLISH);
details.setName("Manu");
List<Map<String, Integer>> allContents = new ArrayList<>();
Map<String, Integer> contents = new HashMap<>();
contents.put("amount", 112);
contents.put("totalPrice", 333);
allContents.add(contents);
contents = new HashMap<>();
contents.put("amount", 222);
contents.put("totalPrice", 444);
allContents.add(contents);
details.setData(allContents);
return details;
}
}
Now when I pass the below query I'm get the output
{
details {
name
locale
data
}
}
Output
{
"data": {
"details": {
"name": "Manu",
"locale": "en",
"data": [
{
"amount": 112,
"totalPrice": 333
},
{
"amount": 222,
"totalPrice": 444
}
]
}
}
}
Required Usecase
But my requirement is to dynamic get only the specified fields from the data object. For example
{
details {
name
locale
data {
amount
}
}
should return the results like as shown below with only the amount details and not showing totalPrice
EXPECTED OUTPUT
{
"data": {
"details": {
"name": "Manu",
"locale": "en",
"data": [
{
"amount": 112
},
{
"amount": 222
}
]
}
}
}
Can someone please tell me if there is a way to do this. Does graphQL supports this kind of use cases
It’s not possible to use a scalar json and to expect graphql to interpret any content of it.
You can, however, use a clever approach by defining what fields in your data you want by other means:
{
details (dataFields: {
amount: true
}) {
name
locale
data
}
}
You should define a dataFields parameter, the type could be simply JSON and then you parse it in Java and include or not the fields based on that json.
In any case it’s not possible to have a JSON that GraphQL can parse.
You should consider creating a proper type for that data instead of a raw JSON if possible.
I am trying to build a pact between two services using asynchronous communication.
This is the code I used for generate the pact:
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "provider", providerType = ProviderType.ASYNCH)
public class StringifiedPactTest {
#Pact(consumer = "consumer", provider = "provider")
public MessagePact generatePact(MessagePactBuilder builder) {
return builder.hasPactWith("provider")
.expectsToReceive("A valid aws sns event")
.withContent(new PactDslJsonBody().stringType(new String[]{"MessageId", "TopicArn"}).stringValue("Message", new PactDslJsonBody().stringType("Value", "Foo").toString()))
.toPact();
}
#Test
#PactTestFor(pactMethod = "generatePact")
public void buildPact(List<Message> messages) {
}
}
And the generated pact is
{
"consumer": {
"name": "consumer"
},
"provider": {
"name": "provider"
},
"messages": [
{
"description": "A valid aws sns event",
"metaData": {
"contentType": "application/json"
},
"contents": {
"TopicArn": "string",
"Message": "{\"Value\":\"Foo\"}",
"MessageId": "string"
},
"matchingRules": {
"body": {
"$.MessageId": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.TopicArn": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.10"
}
}
}
This means the producer should have a "Message" that matches {"Value" : "Foo"}, any other combination like {"Value" : "Bar" } won't be successful.
Is there any way to add matching rules inside a stringified json?
Thanks!
Here's an anonymised example from a test we have. Hope it's useful. This creates a pact that matches only on type. So on the provider side, when I test against the contract, it doesn't matter what value I have for categoryName for example, as long as it's a stringType:
#PactTestFor(providerName = "provider-service", providerType = ProviderType.ASYNCH)
public class providerServiceConsumerPactTest {
private static String messageFromJson;
#BeforeAll
static void beforeAll() throws Exception {
messageFromJson = StreamUtils.copyToString(new ClassPathResource("/json/pact/consumer-service_provider-service.json").getInputStream(), Charset.defaultCharset());
}
#Pact(provider = "provider-service", consumer = "consumer-service")
public MessagePact providerServiceMessage(MessagePactBuilder builder) {
DslPart body = new PactDslJsonBody()
.object("metaData")
.stringType("origin", "provider-service")
.datetimeExpression("dateCreated", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.closeObject()
.minArrayLike("categories", 0, 1)
.stringType("id", "example data")
.stringType("categoryName", "example data")
.booleanType("clearance", false)
.closeObject()
.closeArray();
return builder
.expectsToReceive("a provider-service update")
.withContent(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "providerServiceMessage")
public void testProviderServiceMessage(MessagePact pact) {
// State
final String messageFromPact = pact.getMessages().get(0).contentsAsString();
// Assert
JSONAssert.assertEquals(messageFromPact, messageFromJson, false);
}
I'm having exactly the same issue, and unfortunately I don't think it's possible to tell Pact to parse the stringified JSON and look inside it (e.g. to verify that parse(Message).Value === "Foo" in your example).
The best you can do is write a regular expression to match the string you're expecting. This kind of sucks because there's no easy way to ignore the ordering of the JSON keys (e.g. "{\"a\":\"1\", \"b\":\"2\"}" and "{\"b\":\"2\", \"a\":\"1\"}" will compare different) but AFAIK Pact simply lacks the parsing functionality we're looking for, so the only tool it provides is regex.
I've made one of my API endpoints and inner logic asynchronous and when previously I've used Response.AsJson(Foo.bar()) , it would return the json representation normally, but now I see this appended to it:
{
"result": [
{
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
}
],
"id": 3,
"exception": null,
"status": 5,
"isCanceled": false,
"isCompleted": true,
"isCompletedSuccessfully": true,
"creationOptions": 0,
"asyncState": null,
"isFaulted": false
}
But I want it to be like this:
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
As I understand, it's because I've wrapped my object in a Task , but I can't figure out, how with Nancy framework, which I use the Response.AsJson, to make it so the properties are excluded. I can obviously omit the Response.AsJson of the returned object, but then response is no longer Json if requesting through web-browser for example.
For further example
NancyModule for routing API:
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
var response = await Response.AsJson(itemCatalog.GetItem(id));
return response;
});
}
How the interface looks like of ItemCatalog:
public interface IItemCatalog
{
Task<Item> GetItem(string id);
}
You shoud do this :
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
return Response.AsJson(await itemCatalog.GetItem(id));
});
}
I have a json like this, which i am getting in the response from http call
{
"offset": 0,
"limit": 50,
"objects": [
{
"id": "59118fb6e4b0168ec4b56692",
"modifiedDate": 1494323126886,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Abhimanyu",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "absinghrathore127#gmail.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
},
{
"id": "590d5813e4b03a8336fa1642",
"modifiedDate": 1494046739619,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Tim Archer",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "tim#avocado.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
}
],
"size": 2
}
and i am able to get objects from json via this following code :
String s = res.getBody();
Map<String,Object> jsonMap = (Map<String, Object>)JSON.deserializeUntyped(s);
String jsonSubset = JSON.serialize(jsonMap.get('objects'));
What i need is the value of name and email in some variable.
Please help me out in this!!
This is going to be a tedious task but once you've classified your all data into appropriate Wrapper classes then it's fairly simple and easy to maintain.
First thing is to define your MainWrapper class. This will contain all the at it's own level. If it has any Object as key-pair then we need to make sure to include it as a List<>. So This is how your MainWrapper should be:
public class MainWrapper {
Integer offset; // Singleton variable
Integer limits; // Singleton variable
List<ObjectsWrapper> objects; // Collection variable since it starts with [],
Integer size; // Singleton variable
}
Since you've array of objects in JSON that's why I've included it as a List in MainWrapper. Now it's time to define ObjectsWrapper. Below is wrapper defined for the same.
public class ObjectsWrapper {
String id;
String modifieddate;
String requestedIds;
PropertyWrapper properties;
}
Since there is only on properties associated with objects that's why it's a non-collection type. Below is representation of properties.
public class PropertyWrapper {
List<NameWrapper> name;
List<String> company;
List<String> title;
List<EmailWrapper> email;
String state;
}
public class NameWrapper {
String name;
String metadata;
}
I guess now you've a fair idea of how to organize data of JSON into various wrapper class. Once you're done with this, simply deserialize the JSON into MainWrapper class and access it.
For example:
MainWrapper mainJSONWrapper = (MainWrapper) JSON.deserialize(JSON,MainWrapper.class);
List<ObjectsWrapper> objectsLst = mainJSONWrapper.objects;
for(ObjectsWrapper obj:objectsLst) {
List<NameWrapper> lstNameWrapper = obj.properties;
for(NameWrapper nameObj:NameWrapper) {
System.debug('Name:'+nameObj.name);
System.debug('metadata:'+nameObj.metadata);
}
}
Above code is not tested but yes, it will give idea how you should deserialize JSON in appropriate manner.
Also go through this answer..How to deserialize a JSON String to Apex
In my Angular2-App I´m receiving a JSON-Response via http-Request that kind of looks like that:
{
"documents": [
{
"title": "Example-Doc 1",
"versions": [
{
"fileSize": 15360
},
{
"fileSize": 2048
}
]
},
{
"title": "Example-Doc 2",
"versions": [
{
"fileSize": 15360
},
{
"fileSize": 2048
}
]
}
],
"meta": {
"total": [2]
}
}
Now i wonder how to map this structure into my TypeScript-Classes, i checked different approaches, but it never worked. I actually need the constructor of the Version class to be called.
export class Document {
title: string; // Titel des Dokuments
versions: Version[];
}
If you have complex classes that need to be serialized and deserialized, I suggest that you implement static methods to your classes like fromJson and toJson - however without knowing your classes the rest of the answer will be kind of a guess-work:
Assuming you have a fromJson in place, then you can map your data like the following:
const myDocuments: Document[] = myJson.documents.map(Document.fromJson);
And the fromJson-method could look like this:
class Document {
constructor(public title: string, public versions: Version[])
public static fromJson(json: any): Document {
return new Document(
json.title,
json.versions.map(Version.fromJson)
);
}
}
class Version {
constructor(public fileSize: number) {}
public static fromJson(json: any): Version {
return new Version(json.fileSize);
}
}