Set json=##class(%DynamicAbstractObject).%FromJSONFile("c:\temp\scott.json")
	Set itemIter=json.items.%GetIterator()
	While itemIter.%GetNext(.key, .item) {
		Set identifiersIter=item.identifiers.%GetIterator()
		While identifiersIter.%GetNext(.key, .identifier) {
			If (identifier.typeDiscriminator="ClassifiedId") && (identifier.type.term."en_US"="Scopus Author ID") {
				Write "pureId: ",identifier.pureId,!
				Write "uri: ",identifier.type.uri,!
				
			}
		}
	}

Output:

pureId: xxxxxxxx
uri: /dk/atira/pure/person/personsources/scopusauthor

P.S.: please note that, as posted, the json sample you provide is invalid.

%System/%System/RoutineChange

A process generates a %System/%System/RoutineChange event because a routine has been compiled or deleted. When enabled, this event causes a record to be written to the audit log whenever a routine or class is compiled. The Description field of the audit record includes the database directory where the modification took place, what routine or class was modified, and the word “Deleted” if the routine was deleted.

This Audit Event is available in IRIS, I have no idea if was available back in 2018.

No, it doesn't.

If the variable String contains a Base 64 encoded HL7 message, then:

    Set HL7MessageResponse=##class(EnsLib.HL7.Message).ImportFromString($system.Encryption.Base64Decode(String),.sc)
    ; handle sc error here
    Set DocType=##class(EnsLib.HL7.Schema).ResolveSchemaTypeToDocType(HL7MessageResponse.TypeVersion,HL7MessageResponse.Name)
    Set sc=HL7MessageResponse.PokeDocType(DocType)
    ; handle sc error here

After that, the variable HL7MessageResponse is an instance (OREF, object reference) of an Ens.HL7.Message with proper DocType, Name, etc. that you can use as HL7 response back to your HL7 BP Router.

IMHO the best approach is to take advantage of the  object-oriented development environment that IRIS provide and have the common functions/methods in a single (or multiple) classes, possible abstract classes, and inherit them in the "main" class.

Class Community.perf.ClassMain Extends Community.perf.ClassAbs
{

ClassMethod Compare(NumCalls As %Integer)
{
	Set Start=$zh
	Do ..ClassCalls(NumCalls)
	Set End=$zh
	Write "Class calls: ",End-Start,!
}

ClassMethod ClassCalls(NumCalls As %Integer)
{
	For i=1:1:NumCalls {
		Set x=..Compute(NumCalls)
	}
}

}
Class Community.perf.ClassAbs [ Abstract ]
{

ClassMethod Compute(Num As %Integer)
{
	;Quit Num
	Set ret=Num
	Quit ret
}

}

How about performance?

EPTEST>do ##class(Community.perf.ClassMain).Compare(100000000)
Class calls: 31.675438

In latest version of IRIS (and Cachè?) inherited members method code is no longer duplicated, so there is no difference then using separate classes but I think this approach is more modern, elegant and, depending on situations, MUCH more flexible,

Set ..Adapter.WebServiceClientClass = "CBORDHL7WSService.CBORDHL7Por.....

If you reset the WebServiceClientClass the ..Adapter.%Client property is recreated and the ..Adapter.%Client.HttpRequest.LocalInterface property you modified in OnInit() in lost.

Set the WebServiceClientClass  in the BO settings from the portal and don't change it from code.

If you REALLY need (do you?) to change the WebServiceClientClass  from code (unlikely since you use a constant string), then when you change it:

Set ..Adapter.WebServiceClientClass = "CBORDHL7WSService.CBORDHL7Por.....
If '$IsObject(..Adapter.%Client.HttpRequest) {
    Set ..Adapter.%Client.HttpRequest = ##class(%Net.HttpRequest).%New()
}
Set ..Adapter.%Client.HttpRequest.LocalInterface = $ZStrip($P(..LocalInterface,"("),"*W")
 

I assume that your interoperability is using Application ACK, so the response ACK in traced (there is an Ens.MessageHeader and HL7 Message reponse with ACK).

select req.TargetConfigName as BusinessOperation,
DATEDIFF('ms',req.TimeCreated, res.TimeCreated) as ResponseTime
from %PARALLEL Ens.MessageHeader req, Ens.MessageHeader res
where req.SessionId=res.SessionId
and req.TargetConfigName = ?
and %internal(res.SourceBusinessType)=3
and %internal(req.TargetBusinessType)=3
and req.TimeCreated between ? and ?


First placeholder/parameter (?) is the BO name.

To narrow the scope (and runtime) you can use the second and third placeholder/parameter to limit the date/time range.

Note that the returned ResponseTime will include the time the request remains in queue (if any).

If you have many messages, %PARALLEL will help to speed up.

If the "internal HTTP" is using the PWS (Private Web Server), then NO, NIET, it's a bad, very bad idea.

DO NOT USE THE PWS for anything apart testing and local PC playing.

For ANYTHING serious, install and use a "real" web server (Apache, NGINX or IIS).

It has been so since EVER and has been documented since ever, but it seems that many people still used the PWS.

I guess that was (one of?) the reason that drove InterSystems to completely remove PWS since.....2024.1? (or was 2?).

From Ensemble 2018..3 documentation (the oldest available online):

Note:
When installing Caché and Ensemble, this private version of Apache is installed to ensure that:

The Management Portal runs out of the box.

An out-of-the-box testing capability is provided for development environments.

The private Apache web server is not supported for any other purpose.

For deployments of http-based applications, including CSP, Zen, and SOAP over http or https, you should not use the private web server for any application other than the Management Portal; instead, you must install and deploy one of the supported web servers (for information, see the section “Supported Web Servers” in the online InterSystems Supported PlatformsOpens in a new tab document for this release).

Additional info:

Discontinue Apache web server installations - FAQ

Effective with the first EM release in 2026, the private web server will be discontinued; at that point, upgrades of existing InterSystems IRIS instances will remove the private web server.

If I recall correctly I had a similar issue in some old Ensemble system but I'm not 100% sure the global was really that.

If you can stop the production, then I think it's safe to kill ^Ens.AppData, with the production running I don't think killing it it's a good idea.

A curiosity, do you use EnsLib.SQL.Snapshot class in some SQL host (BO)?
Is the content of ^Ens.AppData somewhat related to EnsLib.SQL.Snapshot?
Looking at the content of ^Ens.AppData, what type of adapter/operation is likely using it?

When I was migrating a system I had to export and import SQL Gateway Connection, so in the source system I exported to a tab delimited file and in the target system I imported the definitions.

The code to create the imported definition is:

	Set SQLConnection=##class(%SQLConnection).%New()
	Set SQLConnection.DSN=$p(line,tab,1)
	Set SQLConnection.Name=$p(line,tab,2)
	Set SQLConnection.ReverseOJ=$p(line,tab,3)
	Set SQLConnection.URL=$p(line,tab,4)
	Set SQLConnection.Usr=$p(line,tab,5)
	Set SQLConnection.bUnicodeStream=$p(line,tab,6)
	Set SQLConnection.classpath=$p(line,tab,7)
	Set SQLConnection.driver=$p(line,tab,8)
	Set SQLConnection.isJDBC=$p(line,tab,9)
	Set SQLConnection.needlongdatalen=$p(line,tab,10)
	Set SQLConnection.noconcat=$p(line,tab,11)
	Set SQLConnection.nodefq=$p(line,tab,12)
	Set SQLConnection.nofnconv=$p(line,tab,13)
	Set SQLConnection.nvl=$p(line,tab,14)
	Set SQLConnection.properties=$p(line,tab,15)
	Set SQLConnection.pwd=$p(line,tab,16)
	Set SQLConnection.useCAST=$p(line,tab,17)
	Set SQLConnection.useCASTCHAR=$p(line,tab,18)
	Set SQLConnection.useCOALESCE=$p(line,tab,19)
	Set SQLConnection.xadriver=$p(line,tab,20)
	Set sc=SQLConnection.%Save()

I'd implement a custom function, create a class like:

Class Community.CustomFunctions Extends Ens.Rule.FunctionSet
{

/// Returns Age in years from DOB in YYYYMMDD format
ClassMethod GetAge(DateOfBirth As %String) As %Integer
{
    Quit (($H-$ZDATETIMEH(DateOfBirth,8)) \ 365.25)
}
/// Returns Age in days from DOB in YYYYMMDD format
ClassMethod GetAgeDays(DateOfBirth As %String) As %Integer
{
	Quit ($H-$ZDATETIMEH(DateOfBirth,8))
}

}

Then from any Rule or DTL transformation you can use these two function as any other built in function.

Make sure DOB is not null ("") before calling the function.

"But the app could be installed in any database, right?"
I believe it's wrong, the app could be installed in any NAMESPACE.

Now the question is, what role have access to the databases associated with the namespace?

Leaving mappings aside, a namespace uses two databases, "Default Database for Globals" and "Default Database for Routines" (code), usually the two databases coincide but you cannot assume it's so.
When I configure two databases for a namespace I use a single resource for both, I consider this a good practice but, again, this cannot be assumed.

A generalized solution should find the resources used by the installation destination namespace.

This is how you can get the databases used by the namespace "MYAPP":

%SYS>Set sc=##Class(Config.Namespaces).Get("MYAPP",.NsProperties)
 
%SYS>Write NsProperties("Routines")
MYAPP-R
%SYS>Write NsProperties("Globals")
MYAPP-G

Now, for each database you can get the associated resource with:

%SYS>Set dbr=##class(SYS.Database).%OpenId(##class(Config.Databases).GetDirectory(NsProperties("Routines")))
 
%SYS>Write dbr.ResourceName
%DB_MYAPP
%SYS>
 
%SYS>Set dbg=##class(SYS.Database).%OpenId(##class(Config.Databases).GetDirectory(NsProperties("Globals")))
 
%SYS>Write dbg.ResourceName
%DB_MYAPP

In this case for the MYAPP namespace you only need permission to the %DB_MYAPP resource.

If the two databases use different resources, then you need permission to both the associated resources.

First you MUST be positively sure on what the character set is actually used in the incoming HL7 message.

What's the character set of the incoming message?

If it's really utf-8 then setting "Default Char Encoding" to utf-8 should work, if it does not, then evidently the incoming message use a different character set. 

Just in case, try setting "Default Char Encoding"  to "!utf8"

For the incoming HL7 messages, does the field MSH:CharacterSet (MSH:18) contains a value? Is so, what's the value?

You write that the receiver message is encoded using utf-8, have you tried to configure the "Default Char Encoding" setting in the Business Service to "utf-8"?

If the incoming HL7 message is actually/really encoded using utf-8 and MSH:18 contains a value different than "utf-8", you can enforce conversion configuring the "Default Char Encoding" setting in the Business Service to "!utf-8".

I hope the message is not malformed and contains data/fields with different codes, if so...it can be tricky.