Sometimes, we need to copy part of the properties of an object into a different one.
The simplest thing would be to do the following:
Set obj1.FirstName = obj2.FirstName
Set obj1.SecondName = obj2.SecondName
What happens if the object contains a large number of properties? or we just want to extract an important group of data, and complement the information in another object?
Having the following classes:
Class Kurro.PersonalInfo Extends %Library.SerialObject
{
/// Code of personal
Property Code As %String;
/// First name of personal
Property FirstName As %String;
/// Second name of personal
Property SecondName As %String;
/// Date of birthday of personal
Property DateOfBirthday As %DateTime;
/// Passport ID number
Property PassportId As %String;
}
Class Kurro.NameInfo Extends %Library.SerialObject
{
/// First name of personal
Property FirstName As %String;
/// Second name of personal
Property SecondName As %String;
/// Date of birthday of personal
Property DateOfBirthday As %DateTime;
/// Relationship
Property Relationship As %String;
}
I've populated the object PersonalInfo
ENSDEMO>zw personal
personal=<OBJECT REFERENCE>[1@Kurro.PersonalInfo]
+----------------- general information ---------------
| oref value: 1
| class name: Kurro.PersonalInfo
| reference count: 2
+----------------- attribute values ------------------
| Code = "FLH01"
| DateOfBirthday = 47878
| FirstName = "Francisco"
| PassportId = "ESP-123456"
| SecondName = "Lopez"
+-----------------------------------------------------
I've created the following utils class
Class Kurro.Utils.Functions
{
/// Copy properties
Method CopyProps(Output obj) As %Status [ CodeMode = objectgenerator ]
{
set properties = %compiledclass.Properties
for i=1:1:properties.Count()
{
#dim prop As %Dictionary.CompiledProperty = properties.GetAt(i)
if prop.Name'="",prop.Name'["%"
{
set propName = prop.Name
if prop.Name["_"
{
set propName = """"_prop.Name_""""
}
do %code.WriteLine(" If (obj.ExistsProperty("""_propName_""")) { set obj."_propName _"=.."_propName_" } ")
}
}
quit $$$OK
}
/// Check if a property exists
ClassMethod ExistsProperty(pPropName As %String = "") As %Boolean
{
Set tExists = 0
If (pPropName '= "")
{
Set tExists = ##class(%Dictionary.CompiledProperty).IDKEYExists($classname($this),pPropName)
}
Quit tExists
}
}
So now we need to extend our classes also to this utils class
Class Kurro.PersonalInfo Extends (%Library.SerialObject, Kurro.Utils.Functions)
Class Kurro.NameInfo Extends (%Library.SerialObject, Kurro.Utils.Functions)
You can check how it works using the following code:
set personal = ##class(Kurro.PersonalInfo).%New()
set personal.Code="FLH01"
set personal.FirstName="Francisco"
set personal.SecondName="Lopez"
set personal.DateOfBirthday=$ZDATEH("1972-02-01",3)
set personal.PassportId ="ESP-123456"
set name = ##class(Kurro.NameInfo).%New()
do personal.CopyProps(.name)
zw name
This is the result:
ENSDEMO>zw name name=<OBJECT REFERENCE>[3@Kurro.NameInfo] +----------------- general information --------------- | oref value: 3 | class name: Kurro.NameInfo | reference count: 2 +----------------- attribute values ------------------ | DateOfBirthday = 47878 | FirstName = "Francisco" | Relationship = "" | SecondName = "Lopez" +-----------------------------------------------------
Important: Only you can copy content to a class that extends to this functions, because it needs the method ExistsProperty.
I hope this trick is as useful to you as it has been to me
Best regards.
Update: This account is discontinued, please follow the new account: @Kurro Lopez
Nice example of using method generators.
Why do you need ExistsProperty? Here's a check I usually employ for class properties in generators:
It filters out all systems properties like Concurrency, %%OID and so on.
Hi,
I've created this method to check if a property exitsts because the method generator doesn't knows if the property exists in the target class in runtime.
If you debug the code you can see the compiled code:
If (obj.ExistsProperty("Code")) { set obj.Code=..Code }
If (obj.ExistsProperty("DateOfBirthday")) { set obj.DateOfBirthday=..DateOfBirthday }
If (obj.ExistsProperty("FirstName")) { set obj.FirstName=..FirstName }
If (obj.ExistsProperty("PassportId")) { set obj.PassportId=..PassportId }
If (obj.ExistsProperty("SecondName")) { set obj.SecondName=..SecondName } }
so, in runtime, there is no a quick way to check if this property exists to prevent an exception error.
Why do you need to check it at runtime? If source and target are from different but related classes you can copy by their common ancestor (or by specified ancestor class). Or do you copy properties between two unrelated classes?
The idea is to copy only the common properties.
If you see the definition of PersonalInfo and NameInfo, both are different classes.
The unique union between both classes is Kurro.Utils.Functions
He does not know what properties the target class obtained, he only knows his own properties and makes a list of methods to copy from the current class to the target class.
This method is compiled, therefore, you need to verify if the destination property exists in the target class at run time. This method is created "on fly" when you create a new instance of this class. Do you know if the property "Name" will be in the object passed as a parameter? Maybe not, so you should check if this property exists in the target.
makes sense?
I hope I have solved the doubt, or maybe I do not understand your question.
Nice one, just notice that your util can copy properties between different types of objects.
Did you checked it with multi dim properties, relationships, arrays, lists, serials, streams, calculated, etc.?
Notice that if obj1 and obj2 are of the same class then use %ConstructClone. It is part of the %Library.RegisteredObject class
http://docs.intersystems.com/latest/csp/docbook/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25sys&CLASSNAME=%25Library.RegisteredObject#METHOD_%ConstructClone
Hi Yuval,
Thanks for your comment.
The idea of this tip is only copy content from a class to another class, only I want a subgroup of elements. The command %ConstructClone is to clone a class, you have the same class, not a part of it.
Best regards