Here's a sample, using %ToDynamicObject (2016.2+):

Class DC.CustomJSONSample extends %RegisteredObject
{
Property myProperty As %String [ InitialExpression = "hello" ];

Property other As %String [ InitialExpression = "world" ];

/// Rename myProperty to custom_property_name
Method %ToDynamicObject(target As %Object = "", ignoreUnknown = 0) [ ServerOnly = 1 ]
{
	Do ##super(.target,.ignoreUnknown)
	Do target.$set("custom_property_name",target.myProperty,target.$getTypeOf("myProperty"))
	Do target.$remove("myProperty")
}

ClassMethod Run()
{
	Set tObj = ..%New()
	Write tObj.$toJSON()
}

}

Output:

SAMPLES>d ##class(DC.CustomJSONSample).Run()
{"other":"world","custom_property_name":"hello"}

For other discussions with solutions/examples involving %ToDynamicObject, see:
https://community.intersystems.com/post/json-cache-and-datetime
https://community.intersystems.com/post/create-dynamic-object-object-id

Hi John,

Thank you for your feedback. We'll address these issues very soon.

I previously hadn't been aware of the ##www.intersystems.com:template_delimiter## syntax, but found it documented here (along with more details about Studio Templates). It looks like we do support this in Atelier for templates themselves (Tools > Templates), but not yet for templates launched from Studio extensions. This should change.

Thanks,

Tim

The problem is that REST uses IO redirection itself, and OutputToStr changes the mnemonic routine but doesn't change it back at the end.

For a great example of the general safe approach to cleaning up after IO redirection (restoring to the previous state of everything), see %WriteJSONStreamFromObject in %ZEN.Auxiliary.jsonProvider.

Here's a simple approach that works for me, in this case:

set tOldIORedirected = ##class(%Device).ReDirectIO()
set tOldMnemonic = ##class(%Device).GetMnemonicRoutine()
set tOldIO = $io
try {
	set str=""

	//Redirect IO to the current routine - makes use of the labels defined below
	use $io::("^"_$ZNAME)

	//Enable redirection
	do ##class(%Device).ReDirectIO(1)

	if $isobject(pObj) {
		do $Method(pObj,pMethod,pArgs...)
	} elseif $$$comClassDefined(pObj) {
		do $ClassMethod(pObj,pMethod,pArgs...)
	}
} catch ex {
	set str = ""
}

//Return to original redirection/mnemonic routine settings
if (tOldMnemonic '= "") {
	use tOldIO::("^"_tOldMnemonic)
} else {
	use tOldIO
}
do ##class(%Device).ReDirectIO(tOldIORedirected)

quit str

It would be cool if something like this could work instead:

new $io
try {
	set str=""

	//Redirect IO to the current routine - makes use of the labels defined below
	use $io::("^"_$ZNAME)

	//Enable redirection
	do ##class(%Device).ReDirectIO(1)

	if $isobject(pObj) {
		do $Method(pObj,pMethod,pArgs...)
	} elseif $$$comClassDefined(pObj) {
		do $ClassMethod(pObj,pMethod,pArgs...)
	}
} catch ex {
	set str = ""
}

quit str

But $io can't be new'd.

You could map the package containing the class related to that table using a package mapping, and the globals containing the table's data using global mappings.

You can see which globals the class uses in its storage definition - since the entire package is mapped, it might make sense to add a global mapping with the package name and a wildcard (*).

After taking those steps, you can insert to the table the way you usually would, without any special syntax or using zn/set $namespace.

For what it's worth, I believe $Namespace is new'd before CreateProjection/RemoveProjection are called. At least, I was playing with this yesterday and there weren't any unexpected side effects from not including:

new $Namespace

in those methods. But it definitely is best practice to do so (in general).

One effect of this I noticed yesterday is that if you call $System.OBJ.Compile* for classes in a different namespace in CreateProjection, they're queued to compile in the original namespace rather than the current one. Kind of weird, but perhaps reasonable; you can always JOB the compilation in the different namespace. Maybe there's some other workaround I couldn't find.

There was a similar question and answer at https://community.intersystems.com/post/how-include-dynaform-custom-task-form-ensemble-workflow that might be helpful.

In short, the simplest solution (and possibly the only one) would be to put the Zen page in an <iframe> in a CSP page that EnsLib.Workflow.TaskRequest.%FormTemplate points to.

I really would recommend creating a %All namespace (if there isn't already one), via %Installer or something that works similarly.

One projects on the intersystems-ru github, Caché Web Terminal, has the same requirement (use from any namespace); this class might be helpful for reference: https://github.com/intersystems-ru/webterminal/blob/master/export/WebTerminal/Installer.xml. It doesn't actually use %Installer, so configuration changes are implemented in COS instead of being generated based on XML, but it works similarly.

Particularly, see methods CreateAllNamespace and Map/UnMap. You should be able to adapt these without too much effort. If your code coverage project eventually has a UI, then the web application setup method will be useful too (for simple installation).

See the documentation on the special %ALL "namespace".

%ALL allows for globals/routines/packages to be mapped to all namespaces. Your %Installer (if you're using one) can check to see if this is set up and create it if not, and add the mappings. You might want to map the packages and perhaps system-wide settings your code uses, but perhaps not any namespace-specific data.

Sure - although it'd be a property, not a parameter. Looking at utcov.ClassLookup (glad to see it's not %utcov now, by the way), this should work fine for you. Here's a sample:

Class Sample.ClassQueryProperty Extends %RegisteredObject
{

Property SubclassQuery As %SQL.Statement [ InitialExpression = {##class(%SQL.Statement).%New()}, Private, ReadOnly ];

Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
	Quit ..SubclassQuery.%PrepareClassQuery("%Dictionary.ClassDefinition","SubclassOf")
}

Method Demo()
{
	Set tRes = ..SubclassQuery.%Execute("%UnitTest.TestCase")
	While tRes.%Next(.tSC) {
		$$$ThrowOnError(tSC)
		Write tRes.%Get("Name"),!
	}
	$$$ThrowOnError(tSC)
}

}

Then:

SAMPLES>d ##class(Sample.ClassQueryProperty).%New().Demo()
%UnitTest.IKnowRegression
%UnitTest.PMMLRegression
%UnitTest.SQLDataRegression
%UnitTest.SQLRegression
%UnitTest.SetBuilder
%UnitTest.TSQL
%UnitTest.TestCacheScript
%UnitTest.TestProduction
%UnitTest.TestScript
%UnitTest.TestSqlScript
Wasabi.Logic.Test.InventoryTest
Wasabi.Logic.Test.PricingTest