Reuse variable name when inserting an Xcode snippet [duplicate] - xcode

Can I create a custom snippet that takes a parameter that is replaced multiple times inside the code?
I tried something like:
<#class#> instanceOf<#class#>;
but it doesn't replace both class placeholders when I insert the snippet and write over the first parameter.

In Xcode 10 we can replace placeholder tokens with the same name by doing this:
Highlight the first token.
Add other tokens with the same name to the selection by pressing ⌥⌘E once for each token. ⌥⇧⌘E selects previous tokens.
Start typing. This activates multiple cursors so each token gets replaced.
It's not as quick as replacing every token with the same name by default, but it does give you more control over what gets replaced.

This is not possible in Xcode 4.x at the time of writing (Sept. 2011).
If you want this feature back in Xcode 4.x, please go to bugreport.apple.com and report a duplicate for this rdar that I just preported:
Summary: Xcode 4 snippets with tokens of same name should sync
while filling one of them.
Steps to Reproduce:
1. Define this snippet: extern NSString * const <#constant#>;
NSString * const <#constant#> = #"<#constant#>";
Drop it into your code.
Hit tab to select instance of token <#constant#>.
Type "NSMySuperCoolConstantString".
Expected Results:
5. NSString * const <#constant#> = #"<#constant#>"; should turn into
NSString * const NSMySuperCoolConstantString =
#"NSMySuperCoolConstantString"; as it used to do in v3.x.
Actual Results:
5. NSString * const <#constant#> = #"<#constant#>"; stays
unchanged/unsynced.
Regression:
Notes:
Here is the rdar reference for duping:
rdar://10071607
And here a copy on OpenRadar:
http://openradar.appspot.com/radar?id=1327411
While you're at it: Feel free to dupe this rdar as well:
"Xcode 4: snippet functionality regressions"
rdar://9192757
http://openradar.appspot.com/9192757

Related

How to solve common errors in Google Apps Script development

The Q&A is currently a subject of meta discussion, do participate. The current plan is to split where possible into Q&As. Answers to the A&A are community wiki and the question should become one when the status is resolved.
Preface
This Q&A strives to become a collection and a reference target for common errors encountered during development in Google Apps Script language in hopes to improve long-term maintainability of google-apps-script tag.
There are several similar and successful undergoings in other languages and general-purpose tags (see c++, android, php, php again), and this one follows suit.
Why it exists?
The amount of questions from both new and experienced developers regarding the meaning and solutions to errors encountered during development and production that can be effectively reduced to a single answer is substantial. At the time of writing, even running a query only by language tag yields:
"Cannot find method" 8 pages
"Cannot read property" 9 pages
"Cannot call ... in this context" 5 pages
"You do not have permission" 11 pages
Linking to a most relevant duplicate is hard and time-consuming for volunteers due to the need to consider nuances and often poorly-worded titles.
What it consists of?
Entries in this Q&A contain are designed to provide info on how to:
parse the error message structure
understand what the error entails
consistently reproduce (where applicable)
resolve the issue
provide a link to canonical Q&A (where possible)
Table of Contents
To help you navigate the growing reference please use the TOC below:
General errors
Service-specific errors
What this is not?
The scope of the Q&A is limited to common (not trivial). This is not:
a catch-all guide or "best practices" collection
a reference for general ECMAScript errors
GAS documentation
a resources list (we have a tag wiki for that)
What to add?
When adding an entry, please, consider the following:
is the error common enough (see "why" section for examples)?
can the solution be described concisely and be applicable for most cases?
Preface
The answer provides a guide on general errors that can be encountered when working with any Google service (both built-in and advanced) or API. For errors specific to certain services, see the other answer.
Back to reference
General errors
Message
TypeError: Cannot read property 'property name here' from undefined (or null)
Description
The error message indicates that you are trying to access a property on an Object instance, but during runtime the value actually held by a variable is a special data type undefined. Typically, the error occurs when accessing nested properties of an object.
A variation of this error with a numeric value in place of property name indicates that an instance of Array was expected. As arrays in JavaScript are objects, everything mentioned here is true about them as well.
There is a special case of dynamically constructed objects such as event objects that are only available in specific contexts like making an HTTP request to the app or invoking a function via time or event-based trigger.
The error is a TypeError because an "object" is expected, but "undefined" is received
How to fix
Using default values
Logical OR || operator in JavaScript has an intersting property of evaluating the right-hand side iff the left-hand is falsy. Since objects in JS are truthy, and undefined and null are falsy, an expression like (myVar || {}).myProp [(myVar || [])[index] for arrays] will guarantee that no error is thrown and the property is at least undefined.
One can also provide default values: (myVar || { myProp : 2 }) guarantees accessing myProp to return 2 by default. Same goes for arrays: (myVar || [1,2,3]).
Checking for type
Especially true for the special case, typeof operator combined with an if statement and a comparison operator will either allow a function to run outside of its designated context (i.e. for debugging purposes) or introduce branching logic depending on whether the object is present or not.
One can control how strict the check should be:
lax ("not undefined"): if(typeof myVar !== "undefined") { //do something; }
strict ("proper objects only"): if(typeof myVar === "object" && myVar) { //do stuff }
Related Q&As
Parsing order of the GAS project as the source of the issue
Message
Cannot convert some value to data type
Description
The error is thrown due to passing an argument of different type than a method expects. A common mistake that causes the error is accidental coercion of a number to string.
How to reproduce
function testConversionError() {
const ss = SpreadsheetApp.getActiveSheet();
ss.getRange("42.0",1);
}
How to fix
Make sure that the value referenced in the error message is of data type required by documentation and convert as needed.
Message
Cannot call Service and method name from this context
Description
This error happens on a context mismatch and is specific to container-bound scripts.
The primary use case that results in the error is trying to call a method only available in one document type (usually, getUi() as it is shared by several services) from another (i.e. DocumentApp.getUi() from a spreadsheet).
A secondary, but also prominent case is a result of calling a service not explicitly allowed to be called from a custom function (usually a function marked by special JSDoc-style comment #customfunction and used as a formula).
How to reproduce
For bound script context mismatch, declare and run this function in a script project tied to Google Sheets (or anything other than Google Docs):
function testContextMismatch() {
const doc = DocumentApp.getUi();
}
Note that calling a DocumentApp.getActiveDocument() will simply result in null on mismatch, and the execution will succeed.
For custom functions, use the function declared below in any cell as a formula:
/**
* #customfunction
*/
function testConversionError() {
const ui = SpreadsheetApp.getUi();
ui.alert(`UI is out of scope of custom function`);
}
How to fix
Context mismatch is easily fixed by changing the service on which the method is called.
Custom functions cannot be made to call these services, use custom menus or dialogs.
Message
Cannot find method Method name here
The parameters param names do not match the method signature for method name
Description
This error has a notoriously confusing message for newcomers. What it says is that a type mismatch occurred in one or more of the arguments passed when the method in question was called.
There is no method with the signature that corresponds to how you called it, hence "not found"
How to fix
The only fix here is to read the documentation carefully and check if order and inferred type of parameters are correct (using a good IDE with autocomplete will help). Sometimes, though, the issue happens because one expects the value to be of a certain type while at runtime it is of another. There are several tips for preventing such issues:
Setting up type guards (typeof myVar === "string" and similar).
Adding a validator to fix the type dynamically thanks to JavaScript being dynamically typed.
Sample
/**
* #summary pure arg validator boilerplate
* #param {function (any) : any}
* #param {...any} args
* #returns {any[]}
*/
const validate = (guard, ...args) => args.map(guard);
const functionWithValidator = (...args) => {
const guard = (arg) => typeof arg !== "number" ? parseInt(arg) : arg;
const [a,b,c] = validate(guard, ...args);
const asObject = { a, b, c };
console.log(asObject);
return asObject;
};
//driver IIFE
(() => {
functionWithValidator("1 apple",2,"0x5");
})()
Messages
You do not have permission to perform that action
The script does not have permission to perform that action
Description
The error indicates that one of the APIs or services accessed lacks sufficient permissions from the user. Every service method that has an authorization section in its documentation requires at least one of the scopes to be authorized.
As GAS essentially wraps around Google APIs for development convenience, most of the scopes listed in OAuth 2.0 scopes for APIs reference can be used, although if one is listed in the corresponding docs it may be better to use it as there are some inconsistencies.
Note that custom functions run without authorization. Calling a function from a Google sheet cell is the most common cause of this error.
How to fix
If a function calling the service is ran from the script editor, you are automatically prompted to authorize it with relevant scopes. Albeit useful for quick manual tests, it is best practice to set scopes explicitly in application manifest (appscript.json). Besides, automatic scopes are usually too broad to pass the review if one intends to publish the app.
The field oauthScopes in manifest file (View -> Show manifest file if in code editor) should look something like this:
"oauthScopes": [
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/userinfo.email",
//etc
]
For custom functions, you can fix it by switching to calling the function from a menu or a button as custom functions cannot be authorized.
For those developing editor Add-ons, this error means an unhandled authorization lifecycle mode: one has to abort before calls to services that require authorization in case auth mode is AuthMode.NONE.
Related causes and solutions
#OnlyCurrentDoc limiting script access scope
Scopes autodetection
Message
ReferenceError: service name is not defined
Description
The most common cause is using an advanced service without enabling it. When such a service is enabled, a variable under the specified identifier is attached to global scope that the developer can reference directly. Thus, when a disabled service is referenced, a ReferenceError is thrown.
How to fix
Go to "Resources -> Advanced Google Services" menu and enable the service referenced. Note that the identifier should equal the global variable referenced.
For a more detailed explanation, read the official guide.
If one hasn't referenced any advanced services then the error points to an undeclared variable being referenced.
Message
The script completed but did not return anything.
Script function not found: doGet or doPost
Description
This is not an error per se (as the HTTP response code returned is 200 and the execution is marked as successful, but is commonly regarded as one. The message appears when trying to make a request/access from browser a script deployed as a Web App.
There are two primary reasons why this would happen:
There is no doGet or doPost trigger function
Triggers above do not return an HtmlOutput or TextOutput instance
How to fix
For the first reason, simply provide a doGet or doPost trigger (or both) function. For the second, make sure that all routes of your app end with creation of TextOutput or HtmlOutput:
//doGet returning HTML
function doGet(e) {
return HtmlService.createHtmlOutput("<p>Some text</p>");
}
//doPost returning text
function doPost(e) {
const { parameters } = e;
const echoed = JSON.stringify(parameters);
return ContentService.createTextOutput(echoed);
}
Note that there should be only one trigger function declared - treat them as entry points to your application.
If the trigger relies on parameter / parameters to route responses, make sure that the request URL is structured as "baseURL/exec?query" or "baseURL/dev?query" where query contains parameters to pass.
Related Q&As
Redeploying after declaring triggers
Message
We're sorry, a server error occurred. Please wait a bit and try again.
Description
This one is the most cryptic error and can occur at any point with nearly any service (although DriveApp usage is particularly susceptible to it). The error usually indicates a problem on Google's side that either goes away in a couple of hours/days or gets fixed in the process.
How to fix
There is no silver bullet for that one and usually, there is nothing you can do apart from filing an issue on the issue tracker or contacting support if you have a GSuite account. Before doing that one can try the following common remedies:
For bound scripts - creating a new document and copying over the existing project and data.
Switch to using an advanced Drive service (always remember to enable it first).
There might be a problem with a regular expression if the error points to a line with one.
Don't bash your head against this error - try locating affected code, file or star an issue and move on
Syntax error without apparent issues
This error is likely to be caused by using an ES6 syntax (for example, arrow functions) while using the deprecated Rhino runtime (at the time of writing the GAS platform uses V8).
How to fix
Open "appscript.json" manifest file and check if runtimeVersion is set to "V8", change it if not, or remove any ES6 features otherwise.
Quota-related errors
There are several errors related to quotas imposed on service usage. Google has a comprehensive list of those, but as a general rule of thumb, if a message matches "too many" pattern, you are likely to have exceeded the respective quota.
Most likely errors encountered:
Service invoked too many times: service name
There are too many scripts running
Service using too much computer time for one day
This script has too many triggers
How to fix
In most cases, the only fix is to wait until the quota is refreshed or switch to another account (unless the script is deployed as a Web App with permission to "run as me", in which case owner's quotas will be shared across all users).
To quote documentation at the time:
Daily quotas are refreshed at the end of a 24-hour window; the exact time of this refresh, however, varies between users.
Note that some services such as MailApp have methods like getRemainingDailyQuota that can check the remaining quota.
In the case of exceeding the maximum number of triggers one can check how many are installed via getProjectTriggers() (or check "My triggers" tab) and act accordingly to reduce the number (for example, by using deleteTrigger(trigger) to get rid of some).
Related canonical Q&As
How are daily limitations being applied and refreshed?
"Maximum execution time exceeded" problem
Optimizing service calls to reduce execution time
References
How to make error messages more meaningful
Debugging custom functions
Service-specific errors
The answer concerns built-in service-related errors. For general reference see the other answer. Entries addressing issues with services listed in official reference are welcome.
Back to reference
SpreadsheetApp
The number of rows in the range must be at least 1
This error is usually caused by calling the getRange method where the parameter that sets the number of rows happens to equal to 0. Be careful if you depend on getLastRow() call return value - only use it on non-empty sheets (getDataRange will be safer).
How to reproduce
sh.getRange(1, 1, 0, sh.getLastColumn()); //third param is the number of rows
How to fix
Adding a guard that prevents the value from ever becoming 0 should suffice. The pattern below defaults to the last row with data (optional if you only need a certain number of rows) and to 1 if that also fails:
//willFail is defined elsewhere
sh.getRange(1, 1, willFail || sh.getLastRow() || 1, sh.getLastColumn());
Error: “Reference does not exist”
The error happens when calling a custom function in a spreadsheet cell that does not return a value. The docs do mention only that one "must return a value to display", but the catch here is that an empty array is also not a valid return value (no elements to display).
How to reproduce
Call the custom function below in any Google Sheets spreadsheet cell:
/**
* #customfunction
*/
const testReferenceError = () => [];
How to fix
No specific handling is required, just make sure that length > 0.
The number of rows or cells in the data does not match the number of rows or cells in the range. The data has N but the range has M.
Description
The error points to a mismatch in dimensions of range in relation to values. Usually, the issue arises when using setValues() method when the matrix of values is smaller or bigger than the range.
How to reproduce
function testOutOfRange() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getActiveSheet();
const rng = sh.getActiveRange();
const vals = rng.getValues();
try {
vals.push([]);
rng.setValues(vals);
} catch (error) {
const ui = SpreadsheetApp.getUi();
ui.alert(error.message);
}
}
How to fix
If it is routinely expected for values to get out of bounds, implement a guard that catches such states, for example:
const checkBounds = (rng, values) => {
const targetRows = rng.getHeight();
const targetCols = rng.getWidth();
const { length } = values;
const [firstRow] = values;
return length === targetRows &&
firstRow.length === targetCols;
};
The coordinates of the range are outside the dimensions of the sheet.
Description
The error is a result of a collision between two issues:
The Range is out of bounds (getRange() does not throw on requesting a non-existent range)
Trying to call a method on a Range instance referring to a non-existent dimension of the sheet.
How to reproduce
function testOB() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getActiveSheet();
const rng = sh.getRange(sh.getMaxRows() + 1, 1);
rng.insertCheckboxes();
}
How to fix
Check that number of rows (getMaxRow()) and columns (getMaxColumns()) are both greater or equal to the parameters passed to getRange() method call and change them accordingly.
Exception: You can't create a filter in a sheet that already has a filter.
Description
The message means that you are trying to call a createFilter method on a Range in a Sheet that already has a filter set (either via UI or script), thus violating the restriction on 1 filter per Sheet, to quote the documentation:
There can be at most one filter in a sheet.
How to reproduce
const testFilterExistsError = () => {
const sh = SpreadsheetApp.getActiveSheet();
const rng = sh.getDataRange();
const filter1 = rng.createFilter();
const filter2 = rng.createFilter();
};
How to fix
Add a guard that checks for the existence of the filter first. getFilter returns either a filter or null if called on a Range instance and is perfect for the job:
const testFilterGuard = () => {
const sh = SpreadsheetApp.getActiveSheet();
const rng = sh.getDataRange();
const filter = rng.getFilter() || rng.createFilter();
//do something useful;
};
UrlFetchApp
Attribute provided with no value: url
Description
The error is specific to UrlFetchApp service and happens when fetch or fetchAll method gets called with an empty string or non-string value.
How to reproduce
const response = UrlFetchApp.fetch("", {});
How to fix
Make sure that a string containing a URI (not necessarily valid) is passed to the method as its first argument. As its common root cause is accessing a non-existent property on an object or array, check whether your accessors return an actual value.

Problem with Objective-C marshalling an "optionals" property in Nativescript

I'm building a NativeScript plugin for iOS to integrate a card payment terminal as an external accessory. It is almost done, and working, but I have problem with passing one argument called "optionals". This is the whole code I'm trying to implement. It's the payworks framework for a Miura terminal. http://www.payworks.mpymnt.com/node/143
MPTransactionParameters *tp = [MPTransactionParameters chargeWithAmount:[NSDecimalNumber decimalNumberWithString:#"5.00"]
currency:MPCurrencyEUR
optionals:^(id<MPTransactionParametersOptionals> _Nonnull optionals) {
optionals.subject = #"Bouquet of Flowers";
optionals.customIdentifier = #"yourReferenceForTheTransaction";
}];
I cannot find a way of sending this "optionals" function.
In the generate typing metadata I see the MPTransactionParametersOptionals is a #protocol, but still don't know how to use it here as a parameter.
This is my current javascript code for the block
const tp = MPTransactionParameters.chargeWithAmountCurrencyOptionals(
amount,
MPCurrencyEUR,
function (optionals) {
console.log(optionals); //logs the newly created MPTransactionParameters instance, with set amount and currency properties, but cannot touch or set the optional properties.
}
);
The 3rd parameter of chargeWithAmountCurrencyOptionals() should be a function, but I'm doing it wrong, and searched everywhere in google how to do it but no success. I'm already trying for 2 days.
It is working, when the 3rd parameter is null, but I need the set the optional properties.
EDIT: adding the metadata. There are a lot of typings for MPtransactionParameters, so I decided to give you the whole file so you can search.
https://drive.google.com/open?id=1kvDoXtGbCoeCT20b9_t2stc2Qts3VyQx
EDIT2: Adding the typings:
https://drive.google.com/open?id=1lZ3ULYHbX7DXdUQMPoZeSfyEZrjItSOS

How to use NSMaxXEdge in Cocoa app with Swift (Xcode 6)?

I've tried to create a Cocoa app which uses NSPopover internally. One of the popover's method is showRelativeToRect: ofView: preferredEdge:, which triggers an popover in Cocoa app.
If you use Objective-C in Xcode 5.1, you can execute the method with the method above, like this:
[popover showRelativeToRect: sender.bounds ofView: sender preferredEdge: NSMaxXEdge];
However, when I tried to use the method in Swift in Xcode 6, the message Use of unresolved identifier 'NSMaxXEdge' was shown when I wrote the following method, which is just a rewrite of the Objective-C method above:
popover.showRelativeToRect(sender.bounds, ofView: sender, preferredEdge: NSMaxXEdge
So where is the NSMaxXEdge gone? The official documentation says it is of type NSRectEdge, but what is the NSRectEdge? The doc doesn't link to NSRectEdge page.
Also, Xcode 5 documentation also says it is NSRectEdge, but again, no link exists there. So how can I know about what it is all about?
And finally, if NSMaxXEdge is no longer available in Xcode 6, what is the alternative?
In Foundation/NSGeometary.h of 10.10 SDK:
typedef enum {
NSMinXEdge = 0,
NSMinYEdge = 1,
NSMaxXEdge = 2,
NSMaxYEdge = 3
} NSRectEdge;
So it should be NSRectEdge.from(2) or NSRectEdge(2) or just pass 2 will be fine.
If you try this:
println("NSRectEdge.max: \(NSRectEdge.max)") // 9223372036854775807
println("NSRectEdge.from(2): \(NSRectEdge.from(2))") // 2
println("NSRectEdge(2): \(NSRectEdge(2))") //2
You will know that .max was actually the max positive signed Int for 64-bit.
(Tho, I can't be sure since it's not really mentioned in documentation anywhere.)
NSRectEdge has a static var called max. I think you should just use that. For example:
popover.showRelativeToRect(rect, ofView: view, preferredEdge: NSRectEdge.max)
(The below is true for beta 5 (developing for 10.9). Your mileage may vary.)
Above, you get a solution that works - passing 0-3 where the function calls for a NSRectEdge. I'd like to pick up on
how can I know about what it is all about?
The documentation indicates that NSRectEdge is bridged with CGRectEdge, but trying to pass in CGRectEdge led to an error of
CGRectEdge is not convertible to NSRectEdge
NSRectEdge.from(2) or NSRectEdge(2) - as suggested above - come back with the error that
NSRectEdge has no accessible initializers
When you type NSRectEdge into Xcode, however, it shows two types:
CGRectEdge (which in this case is useless, see above) and Int.
I'm afraid that you need to guess that NSRectEdge is an enum when you see it both called as a string and defined as an int; but from there to looking up its definition in the framework is a logical step; as is guessing that you will have four values, one for each edge.
Incidentally, there seems to be an internal modulo operation going on somewhere - when you pass it '4', you get NSMinXEdge again and so on.

SoapUI XPath assertion with wildcards

Is there a way to use a wildcard inside an assertion in a XPath test with SoapUI?
I took a look at SoapUI's documentation and they say you can do something like this
<path1>
<path2>*</path2>
</path1>
I checked the 'Allow Wildcards' checkbox.
My question is : I want to assert my date starts with 2012-08-22 but i dont care about the minutes and seconds. I guess my the expression should be something like 2012-08-22* but it doesn't work.
What you are doing sounds like it should work. Here is a quick example i cooked up using a rest service from http://www.geonames.org/export/web-services.html#timezone. I'm using the demo they have supplied
http://api.geonames.org/timezone?lat=47.01&lng=10.2&username=demo
output is
<geonames>
<timezone tzversion="tzdata2012c">
<countryCode>AT</countryCode>
<countryName>Austria</countryName>
<lat>47.01</lat>
<lng>10.2</lng>
<timezoneId>Europe/Vienna</timezoneId>
<dstOffset>2.0</dstOffset>
<gmtOffset>1.0</gmtOffset>
<rawOffset>1.0</rawOffset>
<time>2012-07-25 04:39</time>
<sunrise>2012-07-25 05:50</sunrise>
<sunset>2012-07-25 21:00</sunset>
</timezone>
</geonames>
If you do an xpath match on the result and use the select from current button you get
//geonames/timezone/time
2012-07-25 04:39
If you update this to
//geonames/timezone/time
2012-07-25*
this will work fine and when updating the rest request with a new lat and lng the assertion will still pass since it is not checking the time. If this doesn't help, please supply your full assertion and maybe i could help more.
*note: for soap requests, make sure to declare the namespace and then use the proper format
//ns1:message
It will be sort of a pain, but here is what you can do:
1) Figure out an Xpath 'base' using the assertion tab (sounds like you are here already). I used this public site to test against: http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl
I used the CornerPoints method with 'hawaii' as the single param.
I created this 'base' xpath:
declare namespace ns1='http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl';
declare namespace SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/';
declare namespace SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/';
/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:CornerPointsResponse/listLatLonOut
(it will write the declare statements for you if you click declare)
(which you can test out in the assertions window)
2) Create a Properties step
3) Create a Property transfer step
4) Create a groovy script
5) add a property... i called mine misc
6) add a transfer step
* transfer from the CornerPoints - Request 1 --- Response
* paste the Xpath stuff in the box under the 'transfer from'
* Transfer to your property
(You can test with the little play button)
7) Add something like this to your groovy script:
def x = context.expand( '${Properties#misc}' )
def parts = x.tokenize(',')
for (def part in parts)
{
log.info(part)
if (part.startsWith("-153"))
log.info("good")
}
In the groovy step you can do anything you need to get at your (partial) data. The sample code I added gets lat/lons out of a long line wrapped in CDATA and then checks for just the starting part of some of the data.. just an example.
Remember that you can use groovy and java string methods:
http://groovy.codehaus.org/groovy-jdk/java/lang/String.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html
More groovy tips:
http://www.soapui.org/Scripting-Properties/tips-a-tricks.html

Copy object values in Visual Studio debug mode

In Visual Studio debug mode it's possible to hover over variables to show their value and then right-click to "Copy", "Copy Expression" or "Copy Value".
In case the variable is an object and not just a basic type, there's a + sign to expand and explore the object. It there a way to copy all that into the clipboard?
In the immediate window, type
?name_of_variable
This will print out everything, and you can manually copy that anywhere you want, or use the immediate window's logging features to automatically write it to a file.
UPDATE: I assume you were asking how to copy/paste the nested structure of the values so that you could either search it textually, or so that you can save it on the side and then later compare the object's state to it. If I'm right, you might want to check out the commercial extension to Visual Studio that I created, called OzCode, which lets you do these thing much more easily through the "Search" and "Compare" features.
UPDATE 2 To answer #ppumkin's question, our new EAP has a new Export feature allows users to Export the variable values to Json, XML, Excel, or C# code.
Full disclosure: I'm the co-creator of the tool I described here.
You can run below code in immediate window and it will export to an xml file the serialized XML representation of an object:
(new System.Xml.Serialization.XmlSerializer(obj.GetType())).Serialize(new System.IO.StreamWriter(#"c:\temp\text.xml"), obj)
Source: Visual Studio how to serialize object from debugger
Most popular answer from https://stackoverflow.com/a/23362097/2680660:
With any luck you have Json.Net in you appdomain already. In which
case pop this into your Immediate window:
Newtonsoft.Json.JsonConvert.SerializeObject(someVariable)
Edit: With .NET Core 3.0, the following works too:
System.Text.Json.JsonSerializer.Serialize(someVariable)
There is a extension called Object Exporter that does this conveniently.
http://www.omarelabd.net/exporting-objects-from-the-visual-studio-debugger/
Extension: https://visualstudiogallery.msdn.microsoft.com/c6a21c68-f815-4895-999f-cd0885d8774f
You can add a watch for that object, and in the watch window, expand and select everything you want to copy and then copy it.
By using attributes to decorate your classes and methods you can have a specific value from your object display during debugging with the DebuggerDisplay attribute e.g.
[DebuggerDisplay("Person - {Name} is {Age} years old")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
I always use:
string myJsonString = JsonConvert.SerializeObject(<some object>);
Then I copy the string value which unfortunately also copies the back slashes.
To remove the backlashes go here:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_replace
Then within the <p id="demo">Visit Microsoft!</p> element replace the text with the text you copied.
then replace the var res = str.replace("Microsoft", "W3Schools"); line with
var res = str.replace(/\\/g, '')
Run these new changes but don't forget to click the "try it" button on the right.
Now you should have all the text of the object in json format that you can drop in a json formatter like http://jsonformatter.org or to create a POCO you can now use http://json2csharp.com/
ObjectDumper.NET
This is an awesome way!
You probably need this data for a unit test, so create a Sandbox.cs temporary test or you can create a Console App.
Make sure to get NuGet package, ObjectDumper.NET, not ObjectDumper.
Run this test (or console app)
View test output or text file to get the C# initializer code!
Code:
[TestClass]
public class Sandbox
{
[TestMethod]
public void GetInitializerCode()
{
var db = TestServices.GetDbContext();
var list = db.MyObjects.ToList();
var literal = ObjectDumper.Dump(list, new DumpOptions
{
DumpStyle = DumpStyle.CSharp,
IndentSize = 4
});
Console.WriteLine(literal); // Some test runners will truncate this, so use the file in that case.
File.WriteAllText(#"C:\temp\dump.txt", literal);
}
}
I used to use Object Exporter, but it is 5 years old and no longer supported in Visual Studio. It seems like Visual Studio Extensions come and go, but let's hope this NuGet package is here to stay! (Also it is actively maintained as of this writing.)
Google led me to this 8-year-old question and I ended up using ObjectDumper to achieve something very similar to copy-pasting debugger data. It was a breeze.
I know the question asked specifically about information from the debugger, but ObjectDumper gives information that is basically the same. I'm assuming those who google this question are like me and just need the data for debugging purposes and don't care whether it technically comes from the debugger or not.
I know I'm a bit late to the party, but I wrote a JSON implementation for serializing an object, if you prefer to have JSON output. Uses Newtonsoft.Json reference.
private static void WriteDebugJSON (dynamic obj, string filePath)
{
using (StreamWriter d = new StreamWriter(filePath))
{
d.Write(JsonConvert.SerializeObject(obj));
}
}
I've just right clicked on the variable and selected AddWatch, that's bring up watch window that consists of all the values. I selected all and paste it in a text a text editor, that's all.
Object Dumper is a free and open source extension for Visual Studio and Visual Studio Code.
"Dump as" commands are available via context menu in the Code and Immediate windows.
It's exporting objects to:
C# object initialization code,
JSON,
Visual Basic object initialization code,
XML,
YAML.
I believe that combined with the Diff tool it can be helpful.
I'm the author of this tool.
if you have a list and you want to find a specific variable:
In the immediate window, type
myList.Any(s => s.ID == 5062);
if this returns true
var myDebugVar = myList.FirstOrDefault(s => s.ID == 5062);
?myDebugVar
useful tips here, I'll add my preference for when i next end up here asking this question again in the future.
if you don't mind adding an extension that doesn't require output files or such there's the Hex Visualizer extension for visual studio, by mladen mihajlovic, he's done versions since 2015.
provides a nice display of the array via the usual magnifine glass view object from the local variables window.
https://marketplace.visualstudio.com/items?itemName=Mika76.HexVisualizer2019 is the 2019 version.
If you're in debug mode, you can copy any variable by writing copy() in the debug terminal.
This works with nested objects and also removes truncation and copies the complete value.
Tip: you can right click a variable, and click Copy as Expression and then paste that in the copy-function.
System.IO.File.WriteAllText("b.json", page.DebugInfo().ToJson())
Works great to avoid to deal with string debug format " for quote.
As #OmerRaviv says, you can go to Debug → Windows → Immediate where you can type:
myVariable
(as #bombek pointed out in the comments you don't need the question mark) although as some have found this limits to 100 lines.
I found a better way was to right click the variable → Add Watch, then press the + for anything I wanted to expand, then used #GeneWhitaker's solution, which is Ctrl+A, then copy Ctrl+C and paste into a text editor Ctrl+V.

Resources