I'm trying to get a value from an API and usually I get it through a list and just use an index and the String to get the info I need (e.g. data[index]["String"]), however this API sends a Map<String, dynamic> and I used the key to retrieve part of the Map (data["key"]}) but I am trying to be more specific in the value. Is there a way to get the exact value, like data["key"]["String"]?
You can use json.decode on your data
Example :
var response = await /* httpcall */
var data = json.decode(response.body);
Depending on the structure of your response you might have to change the field that you json encode, but at least you can now use data['foo']['bar']
Related
Sorry for the newbie question. Learning Apex here. Been working on an issue for several hours and can't seem to needle it out.
I have a JSON that I need converted to a List... the JSON is retrieved via an API. The code is pretty simple, it is only a couple of lines.
The JSON looks like this:
{"id":1,"abbreviation":"ATL","city":"Atlanta","conference":"East","division":"Southeast","full_name":"Atlanta Hawks","name":"Hawks"}
And the code I am told to use to retrieve it looks like this:
Map<String, Object> resultsMap = (Map<String, Object>) JSON.deserializeUntyped(results.getBody());
Based on the JSON provided, there does not appear to be a MAP key being assigned, so I have no idea how to get it so that I may assign it to a List...
I've already tried assigning it directly to a List, but I didn't get much success there either.
I've tried this already:
List<Object> other = (List<Object>) results.get('');
I've also tried this:
List<Object> other = (List<Object>) results.keySet()[0];
I'm sure it is something simple. Any help would be appreciated.
Thanks
You cant convert the map to a list without an id. Alternatively why to convert it to List. Use the map itself. Below is a code example of how you can use it using your JSON response. For example, I want to insert this JSON response into the contact record, so I can map it using the MAP itself.:
#RestResource(urlMapping='/URIId/*')
global class OwneriCRM {
#HttpPut
global static String UpdateOwnerinfo(){
RestRequest req= RestContext.request;
RestResponse res= RestContext.response;
res.addHeader('Content-type','application/JSON');
Map<String,Object> responseresult=new Map<String,Object>();
Map<String,Object> results= (Map<String,Object>)JSON.deserializeUntyped(req.requestBody.toString());
List<contact> insertList = new List<contact>();
for (String key : results.keySet()){
System.debug(results.get(key));
contact c= new contact();
c.ContactField__C = results.get(id);
c.ContactField1__C = results.get(city);
c.ContactField2__C = results.get(conference);
c.ContactField3__C = results.get(division);
c.ContactField3__C = results.get(full_name);
c.ContactField3__C = results.get(name);
insertList.add(c);
}
if(!insertList.Isempty()){
update insertList;
}
Question can be disregarded. Not sure if there is a way to withdraw the question.
Question was based on the fact that previous APIs had been sent in the following format:
{"data": {"more stuff": "stuff"} }
And the map key was 'data' with a list. In this scenario, the whole API was a list and the keys to the map were set in place with the actual values instead.
Using a GET in postman with the URL posted below, I am able to store the entire response header in question with all of its data in a var, the issue for me is how do I verify the pieces of data inside that var
here is my URL
http://localhost/v1/accounts?pageNumber=1&pageSize=2
[
using postman I am able to get the above in a var
var XPaginationData = postman.getResponseHeader(pm.globals.get("PaginationHeader"));
pm.globals.set("XPaginationData", XPaginationData);
is there a way to get the individual values inside the response header X-Pagination stored in a different var to assert later
using this in postman
pm.globals.set("XPaginationData", JSON.stringify(pm.response.headers));
console.log(JSON.parse(pm.globals.get('XPaginationData')));
console.log(JSON.parse(pm.globals.get('XPaginationData'))[4].value);
I get
how would i go about getting "TotalCount" for example
BIG EDIT:
thanks to a coworker, the solution is this
//Filtering Response Headers to get PaginationHeader
var filteredHeaders = pm.response.headers.all()
.filter(headerObj => {
return headerObj.key == pm.globals.get("PaginationHeader");
});
// JSON parse the string of the requested response header
// from var filteredHeaders
var paginationObj = filteredHeaders[0].value;
paginationObj = JSON.parse(paginationObj);
//Stores global variable for nextpageURL
var nextPageURL = paginationObj.NextPageLink;
postman.setGlobalVariable("nextPageURL", nextPageURL);
You could use JSON.stringfy() when saving the environment variable and then use JSON.parse() to access the different properties or property that you need.
If you set a global variable for the response headers like this:
pm.globals.set('PaginationHeader', JSON.stringify(pm.response.headers))
Then you can get any of the data from the variable like this:
console.log(JSON.parse(pm.globals.get('PaginationHeader'))[1].value)
The image shows how this works in Postman. The ordering of the headers returned in the console is inconsistent so you will need to find the correct one to extract data from the X-Pagination header
Looks like an issue with Postman itself.
The only solution that worked for me was to stringify & parse the JSON again, like this:
var response = JSON.parse(JSON.stringify(res))
After doing this, the headers and all other properties are accessible as expected.
I am trying to save data into the dynamoDb but that data contains some Map attributes as well.But I am getting error while saving that data. Following is my domain class which I am using for saving data from request:
#DynamoDBTable(tableName = "ottMiddleware_rails")
public class RailsCmsDomain {
#DynamoDBHashKey(attributeName = "railId")
private String railId;
#DynamoDBTyped
#DynamoDBAttribute(attributeName = "railLogic")
private Map<String, Object> railLogic;
#DynamoDBAttribute(attributeName = "railSourceType")
private String railSourceType;
#DynamoDBAttribute(attributeName = "railTitle")
private RailCmsTitleDomain railTitle;
#DynamoDBTyped
#DynamoDBAttribute(attributeName = "restrictions")
private Map<String, Object> restrictions;
I am giving the following request:
{
"railId": "railOne",
"railLogic": {
"programType": 1,
"railSourceUrl": "http://myUrl"
},
"railSourceType": "myRail",
"railTitle": {
"tam": "Raan Phan",
"def": "சிறப்பு கட்டமைப்பு"
},
"restrictions": {
"clients": [
"abc",
"xyz"
],
"periodStart": 1506572217
}
}
I am using following code to save my data into the dynamoDb
public Boolean saveUpdateRailsDetails(RailsCmsDomain railsDomain) {
DynamoDBUtil dynamoDBUtil = new DynamoDBUtil();
AmazonDynamoDB dynamoDBClient = dynamoDBUtil.getDynamoDBClient();
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
mapper.save(railsDomain);
return true;
}
Please suggest how can I save map into dynamoDb. I am taking data as map because in later stages there are chances more data can be added to those attributes which are map and that data can be of any data type. I am getting following error:
errorMessage": "not supported; requires #DynamoDBTyped or
#DynamoDBTypeConverted"
"errorType":
"com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException"
I would like to post this answer as an option though it is not going to store the data as map in DynamoDB. However, it will resolve the error.
You can define the restrictions attribute as mentioned below. It will store the data as JSON string in DynamoDB.
#DynamoDBTypeConvertedJson
private Map<String, Object> restrictions;
Drawbacks:-
When you would like to update the restrictions attribute, you need to get the current value from database, update it and save the data in database.
DynamoDB data:-
restrictions attribute saved as JSON string.
When I needed to store map in dynamodb I just annotated the getter method of the map with #DynamoDBTypeConverted(converter = MapConverter.class) and in the converter just serialized the map into a json string, this way you can retrieve the map and everything but are not going to be able to use it in queryExpressions if you need.
So in case you want to be able to query the objects in the map maybe consider annotating the object you are using in your map with #DynamoDBDocument this way it's going to be automatically serialized by dynamodb as a document and use Set instead of map into your parent object.
The problem is that the DynamoDB Mapper doesn't know how to marshall/unmarshall the Object in this line:
private Map<String, Object> railLogic;
If you don't want to change Object to String, then use the DynamoDBTypeConverted annotation to provide a custom marshaller for Map<String, Object> which you can then (probably) reuse for restrictions. Relevant documentation here.
I am trying to fetch items from the dynamoDb. However when I fetch single item using partiton key everything works fine but when I try to fetch all the items in dynamo db using scan I encouter an error. Following is the code what I am trying to do:
public List<PageCmsDomain> getAllPages() {
DynamoDBUtil dynamoDBUtil = new DynamoDBUtil();
AmazonDynamoDB dynamoDBClient = dynamoDBUtil.getDynamoDBClient();
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
List<PageCmsDomain> scanResult = mapper.scan(PageCmsDomain.class, new
DynamoDBScanExpression());
return scanResult;
Following error comes when I execute this code:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: PageCmsDomain[restrictions]; could not unconvert attribute
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel.unconvert(DynamoDBMapperTableModel.java:271)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.privateMarshallIntoObject(DynamoDBMapper.java:456)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.marshallIntoObjects(DynamoDBMapper.java:484)
at com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList.<init>(PaginatedScanList.java:64)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.scan(DynamoDBMapper.java:1458)
at com.amazonaws.services.dynamodbv2.datamodeling.AbstractDynamoDBMapper.scan(AbstractDynamoDBMapper.java:216)
at com.astro.ott.dynamodb.cmsapi.impl.PageCmsDaoImpl.getAllPages(PageCmsDaoImpl.java:74)
at com.astro.ott.cmsapi.impl.PageCmsGetServiceImpl.getPages(PageCmsGetServiceImpl.java:41)
at com.astro.ott.cms_api.cms_api.PageCmsTestCase.get(PageCmsTestCase.java:81)
Caused by: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted
at com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$Rules$NotSupported.get(StandardModelFactories.java:660)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$Rules$NotSupported.get(StandardModelFactories.java:650)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$AbstractRule.unconvert(StandardModelFactories.java:714)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$AbstractRule.unconvert(StandardModelFactories.java:691)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter$DelegateConverter.unconvert(DynamoDBTypeConverter.java:109)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters$Vector$ToMap.unconvert(StandardTypeConverters.java:433)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters$Vector$ToMap$1.unconvert(StandardTypeConverters.java:417)
at com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters$Vector$ToMap$1.unconvert(StandardTypeConverters.java:410)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter$DelegateConverter.unconvert(DynamoDBTypeConverter.java:109)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter$NullSafeConverter.unconvert(DynamoDBTypeConverter.java:128)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter$ExtendedConverter.unconvert(DynamoDBTypeConverter.java:88)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.unconvert(DynamoDBMapperFieldModel.java:146)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.unconvertAndSet(DynamoDBMapperFieldModel.java:164)
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel.unconvert(DynamoDBMapperTableModel.java:267)
... 32
And I am using same PageCmsDomain while fetching single item also.
Specified code will work only if restrictions field is null or empty map. Otherwise it will throw exception as DynamoDB does not allow to save or fetch objects that have field with Object type (even as a generic type, in this case - map's value type Object), instead it should have specific type (e.g. String, Integer, Map<String, Integer>).
If restrictions map's value type can't be changed to specific one,
you can fix that by adding DynamoDBTypeConvertedJson annotation to store serialized value of restrictions field:
#DynamoDBAttribute(attributeName = "restrictions")
#DynamoDBTypeConvertedJson
private Map<String, Object> restrictions;
Another option is to specify your own custom converter class and annotate restrictions field with
#DynamoDBTypeConverted(converter = CustomConverter.class)
I have record type "XYZ" which has field called "award area" which is of type list/record. "award area" is of type custom list and is a drop down control.
Using Suitetalk how can I retrieve those values from that drop down?
Thank you
I think something like this should work. It's for translating the results from the internalId's returned into the actual text type, you maybe be able to leverage it in another way. Maybe you could create a lookup list with something like this(C#):
public Dictionary<string, Dictionary<long, string>> getCustomFieldLists()
{
return
nsService.search(new CustomListSearch())
.recordList.Select(a => (CustomList) a)
.ToDictionary(a => a.name,
a => a.customValueList.customValue
.ToDictionary(b => b.valueId, c => c.value));
}
var valueLookup = getCustomFieldLists()["award area"];
Here's how I did it for myself, because I was irritated with the fact the NetSuite doesn't just provide us an easy way to access these. And I wanted the following data for reference:
The Internal ID of the Custom List
The Name of the Custom List
The Internal ID of the Custom List Item
The name Value of the Custom List Item
I wanted/needed access to all of those things, and I wanted to be able to obtain the name Value of the Custom List Item by just providing the Internal ID of the Custom List and the Internal ID of the Custom List Item. So, in my homemade integration client, similar to David Rogers' answer, but without all the fancy Linq, I figured out that the best solution was a Dictionary>>.
This way, for the outer Dictionary, I could set the key to the internal IDs of the Custom Lists, and for the inner Dictionary I could set the key to the internal IDs of the Custom List Items themselves. Then, I would get the name of the Custom List for "free" as the beginning part of the Tuple, and the actual name Value for "free" as the value of the internal Dictionary.
Below is my method code to generate this object:
/// <summary>
/// Gets the collection of all custom lists, and places it in the public CustomListEntries object
/// </summary>
/// <returns></returns>
private Dictionary<string, Tuple<string, Dictionary<long, string>>> GetCustomLists()
{
Dictionary<string, Tuple<string, Dictionary<long, string>>> customListEntries = new Dictionary<string, Tuple<string, Dictionary<long, string>>>();
SearchPreferences sp = SuiteTalkService.searchPreferences; //Store search preferences to reset back later, just need body fields this one time
SuiteTalkService.searchPreferences = new SearchPreferences() { bodyFieldsOnly = false };
SearchResult sr = SuiteTalkService.search(new CustomListSearch());
SuiteTalkService.searchPreferences = sp; //Restore search preferences
foreach (CustomList cl in sr.recordList)
{
Dictionary<long, string> customListItems = new Dictionary<long, string>();
if (cl.customValueList == null) continue;
foreach (CustomListCustomValue clcv in cl.customValueList.customValue)
{
customListItems.Add(clcv.valueId, clcv.value);
}
customListEntries.Add(cl.internalId, new Tuple<string, Dictionary<long, string>>(cl.name, customListItems));
}
return customListEntries;
}
Then, in the constructors of my Integration class, I can set my object to the return result:
public Dictionary<string, Tuple<string, Dictionary<long, string>>> CustomListEntries = GetCustomLists();
And finally, whenever I need access TO those values, since I set all of this up ahead of time, I can do the following:
dr[Class] = SuiteTalkIntegrator.CustomListEntries[lorr.typeId].Item2[long.Parse(lorr.internalId)];
In this case above, my "lorr" object is a ListOrRecordRef object that I obtained from the SearchColumnSelectCustomField.searchValue from the search results of a SavedSearch. I don't know if this will work for anyone else that finds this code, but since I was frustrated in finding an easy answer to this problem, I thought I'd share my solution with everyone.
Frankly, I'm most frustrated that this functionality isn't just given to us out of the box, but I've noticed that NetSuite has made a lot of bad design choices in their SuiteTalk API, like not making a custom class of "RecordField" for their record fields and not placing their record fields under an IEnumerable of RecordField so that programmers can loop through all values in a record in a generic way without having to EXPLICITLY name them and re-construct the same code logic over and over again... ugh...