Working with Powershell invoke-restmethod and json response - json

VERY newbie here with Powershell and JSON so may need a little extra help. Not a coder, fyi.
I'm working with Dell's API to retrieve warranty information on machines. I'd like to do it in Powershell if possible.
Here's my code so far -
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("apikey", 'mykey')
$response = Invoke-RestMethod https://sandbox.api.dell.com/support/assetinfo/v4/getassetwarranty/servicecode -Headers $headers -contenttype 'application/json'
If I look at $response, here is the output -
AssetWarrantyResponse : {#{AssetHeaderData=; ProductHeaderData=; AssetEntitlementData=System.Object[]}}
InvalidFormatAssets : #{BadAssets=System.Object[]}
InvalidBILAssets : #{BadAssets=System.Object[]}
ExcessTags : #{BadAssets=System.Object[]}
AdditionalInformation :
Did some digging and found if I use the below code, I can start seeing actual data -
$response.AssetWarrantyResponse|fl
AssetHeaderData : #{BUID=11; ServiceTag=....
ProductHeaderData : #{SystemDescription=Op....
AssetEntitlementData : {#{StartDate=2017-11-2....
I see there is a command for convertfrom-json. Looks like this will take the response and make it a little easier to work with in Powershell. But I can't for the life of me make this work. I'm trying to pull out a few pieces of data from the AssetHeaderData and AssetEntitlementData above. I'm sure I could do some regex's, but if there's an easier way, I'm all for it!
Any help is appreciated. Thanks!

You're doing great! What you're seeing is PowerShell's system of displaying data on the screen. Since PowerShell tends to deal in complex objects, it's not just formatted string output you are dealing with like you might see from regular shell commands.
In this case, Invoke-RestMethod has automatically done the equivalent of ConvertFrom-Json for you, that is, it has taken the JSON returned from the API and created an object out of it.
The object has various properties, like .AssetWarrantyResponse, each of which could be a value or an array or another object.
So what you're seeing in the output, in your first dump of $response in your question, is the properties the response object has, and the values of those properties.
The values look funny because they are themselves objects, some of which contain arrays, and they are difficult to format all at once.
You've already figured out that you can reference the properties directly, for example by using $response.AssetWarrantyResponse.
When you look at that, you see more properties, which are also objects. I don't think it's going to be nested much more deeply based on the output you posted.
So to look at what's inside the properties you want, just keep digging away:
$response.AssetWarrantyResponse.AssetHeaderData
$response.AssetWarrantyResponse.AssetEntitlementData
You don't need regex for this at all, because the data is already broken up appropriately.
If there's something else you want to do with it, edit it into your question (or post a new one if warranted) and we can help with that too.
Update based on comment
Tab completion can be finicky. But as I look at your data again, I think I see why you saw what you saw, and tab completion was working correctly.
AssetWarrantyResponse is a single property, it will tab complete. Its value is an array. So trying to do .AssetWarrantyResponse.AssetHeaderData will not tab complete, because it's not a property of the object (an array).
If you did $response.AssetWarrantyResponse[0].AssetHeaderData then tab completion would work.
The reason it "works" when just typing it, is because of a change in PowerShell v3.
Before v3, if you have an array of objects and wanted to get an array of the .Thing property of each object, you would have to enumerate it yourself:
$things = foreach($item in $arr) {
$arr.Thing
}
v3 added a neat feature where you could just do $things = $arr.Thing and it handles it all for you. But since each item in the array might be completely different and have different properties, tab completion doesn't try to go into every one of them and read them and decide what to tab complete; you'd have to dereference yourself a single item and then tab completion will work as expected.
So even if you want to use the v3 goodness, you can use tab completion with a little extra typing:
$response.AssetWTAB[0].AssetHTAB
Then just go back and delete [0].
Just remember that if that property is an array, you should probably expect that .AssetWarrantyResponse.AssetHeaderData could also be an array, and so you must deal with that case appropriately in whatever comes next.
If you don't care about that situation and just want the first item, use [0] (or use [-1] if you only want the last one). Point is, if you want to assume it will only ever be a single element, make sure the code handles that assumption so it doesn't do strange things on an edge case.

Related

Squarespace access JSON properties via URL?

I know that I can access the JSON data like so http://base-template.squarespace.com/news/?format=json-pretty. But what I want to access lets says a property on that like news.items? http://base-template.squarespace.com/news/items/?format=json-pretty throw an error. Is there a way to drill into the JSON data via the URL?
Yes, you can access a collection's items using the same format=json-pretty query parameter as you mentioned.
Do note, however, that:
The URL must exist (which in your second 'news' example is not the case)
To get a list of items from a collection, you use the format=json-pretty on the collection, the scope into items from there.
When performing this type of request via Javascript, you'll probably want to simply use format=json since the 'pretty' line breaks and indenting aren't necessary in that case.
For example, using the base-template as you have already mentioned, to get the blog items you would use: https://base-template.squarespace.com/blog?format=json-pretty. Within the JSON, you'll see an 'items' array, which is the data you're looking for (See the image below for a screenshot of this.). If that website had a "/news" collection, you could do similar.
Similarly, if you wanted to view the JSON output from a specific item, you would, for example, use: https://base-template.squarespace.com/blog/2016/7/15/most-recent-sample-blog-post?format=json-pretty

Deserialize an anonymous JSON array?

I got an anonymous array which I want to deserialize, here the example of the first array object
[
{ "time":"08:55:54",
"date":"2016-05-27",
"timestamp":1464332154807,
"level":3,
"message":"registerResourcePath ('', '/sap/bc/ui5_ui5/ui2/ushell/resources/')",
"details":"","component":"sap.ui.ModuleSystem"},
{"time":"08:55:54","date":"2016-05-27","timestamp":1464332154808,"level":3,"message":"URL prefixes set to:","details":"","component":"sap.ui.ModuleSystem"},
{"time":"08:55:54","date":"2016-05-27","timestamp":1464332154808,"level":3,"message":" (default) : /sap/bc/ui5_ui5/ui2/ushell/resources/","details":"","component":"sap.ui.ModuleSystem"}
]
I tried deserializing using CL_TREX_JSON_SERIALIZER, but it is corrupt and does not work with my JSON, here is why
Then I tried /UI2/CL_JSON, but it needs a "structure" that perfectly fits the object given by the JSON Object. "Structure" means in my case an internal table of objects with the attributes time, date, timestamp, level, messageanddetails. And there was the problem: it does not properly handle references and uses class description to describe the field assigned to the field-symbol. Since I can not have a list of objects but only a list of references to objects that solution also doesn't works.
As a third attempt I tried with the CALL TRANSFORMATION as described by Horst Keller, but with this method I was not able to read in an anonymous array, and here is why
My major points:
I do not want to change the JSON, since that is what I get from sap.ui.log
I prefere to use built-in functionality and not a thirdparty framework
Your problem comes out not from the anonymity of array, but from the awkwardness of SAP JSON (De)serializer, which doesn't respect double quotes, which enclose JSON attributes. The issue is thoroughly described in this answer.
If you don't want to change your JSON on-the-fly, the only way you have is to change CL_TREX_JSON_DESERIALIZER class like this.
/UI5/CL_JSON_PARSER parses JSONs with unknown format.
Note that it's got "for internal use" written on it so many times that you probably should take it seriously and clone its code to fixate it.

Sort BC WebApp on front end using a custom field

I have created a BC WebApp using the BC Open Platform API's for the back end and everything appears to work fine including rendering the list of items in a sort order of one of my custom fields.
Here is an example of what works on the back end.
var items = new BCAPI.Models.WebApp.ItemCollection(WEBAPP_NAME);
items.fetch({
order: "MyCustomField",
skip: 0,
limit: 1000,
success: onWebpAppListFetch,
error: onAPIError
});
How do I render this list on the front end sorted by one of my custom fields? Here is an example of what I am trying to use on the front end, but it does not order or sort this way.
{module_webapps order="MyCustomField" render="collection" template="/_System/apps/cms-sports-club-manager/club-rooms/layouts/club_rooms_collection.tpl" id="cms-club-roomsx" filter="all"}
Is there something that I am overlooking, or do I need to approach this in my own manual way? Perhaps I could render the list into an array, sort the array and then iterate through that to render the front end listing? The template file uses Liquid to iterate through the collection and render the HTML. Can I define an array variable, fill the array, sort the array and iterate through the array again in that same template file?
Another possibility perhaps is to output all webapp items into a JSON file each time a user creates/edits an item (from the back end), and then use the {module_json} feature on the front end to read that JSON file which "should" allow me to sort it.
Any advice on what to do (as well as what NOT to do) would be appreciated.
I have found one possible answer, but I am not sure it is the best way to do this.
I have implemented a script which performs the following steps:
Create an array holding all items (unsorted at this point)
Sort the array using any custom field
Iterate through the sorted array and document.write() each one
I have tested this a fair bit and it seems to do exactly what I want, however I have not marked this as the correct answer just yet as I would still like to find a "better" way than needing to resort to the above manual work.

Using localstorage

Got myself in a tricky situation. I'm using local storage to save values from a popup window, and then paste them into an input when focus returns to parent window.
But then something rather awkward takes place, when I try to store ';' separated values, is that I get only the 1st set, losing all the rest of the string. What makes it more bizarre is that after saving my value, I test by calling
alert('SELECTED : ' + localStorage.getItem('MyStr'));
the whole string is there... but on the script I retrieve this value, when i'm checking
alert(localStorage.getItem('MyStr'));
Only the 3rd set is there, i.e.: I store something like
abcdefg;123323;ffasfs;5445;iuiuifa;
but when I need to get it back, theres only
ffasfs
I could use some help then, I'm all new to this whole thing, and killing myself to get a website working.
Thanks in advance, sorry if my question looks stupid.
Store your values in localStorage as JSON strings. This may even help you build more complex objects for the future.
For now though... Just do:
localStorage.setItem("your key", JSON.stringify("abcdef;1234;whatever"));
This procedure will not only sanitize your input but also create oppertunity to store serialized objects in the future.
It's important to note that while JSON.stringify is pretty much supported everywhere... Not all browsers have it built in.
For those cases, check out json2.js.
Hope that helps.

JSON output to the browser -> providing an order

So I've read that you cannot expect a default order when requesting json. I've seen this in action making a call to a little api that I built, that will return a jumbled, random order of elements each time I make a different call.
How does a site like ticketfly's api ( call it here http://www.ticketfly.com/api/events/upcoming.json?venueId=57 ) always ensure that the json returned is in a specific order?
The event ids always first, etc.
Thanks for shedding some light on the situation.
If you are in control of the endpoint API then you can hardcode the order in which you render the properties. Though I have to ask why exactly do you need the JSON properties in a particular order? You will finally be accessing the properties via there property names so the order in which they appear in the JSON should not ideally matter.
EDIT : Since your bosses insist on this (what can one say now?):
You can try and see if any of the following suits your needs:
Try hardcoding the display order in the view's representation. This means you will need to echo/print each property name explicitly in the view script. In PHP it could be something like echo $variable_representing_json["id"]; and so forth. Note that with this approach you needn't change the original JSON representation.
If you want the original JSON representation to be changed then depending on how you are doing the process it varies in difficulty:
If it's string concatenation that you are using to represent the json then hard-code the order in which the json properties get concatenated in the string.
In some languages the display order of properties is actually a representation of the order in which the properties were defined. In simple words if $var is an empty json representation then you should define $var["id"] = {some_val} first to display it first.
If you are using a framework for processing the JSON data it may have its own quirks irrespective of how you define your representation. In such cases you will have to try and see if you can work around the issue or if it gives any helper methods.