Question
· Jan 12, 2021

How Can I get a list fo the classes from a package and each item XData content?

Hi community,

How Can I get a list fo the classes from a package and for each item of the list the XData content?

Product version: IRIS 2020.3
Discussion (3)2
Log in or sign up to continue

Here's a quick sample:

Class DC.Demo.XDataDemo
{

ClassMethod Driver()
{
    Set array = ..GetXDataContents("DC.Demo")
    zw array
}

ClassMethod GetXDataContents(package As %String) As %Library.ArrayOfObjects
{
    $$$ThrowOnError($System.OBJ.GetPackageList(.classes,package))
    Set array = ##class(%Library.ArrayOfObjects).%New()
    Set class = ""
    For {
        Set class = $Order(classes(class))
        Quit:class=""
        Set xDataName = ""
        For {
            Set xDataName = $$$defMemberNext(class,$$$cCLASSxdata,xDataName)
            Quit:xDataName=""
            Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen(class,xDataName,,.sc)
            $$$ThrowOnError(sc)
            Do array.SetAt(xdata.Data,class_":"_xDataName)
        }
    }
    Quit array
}

XData Foo
{
}

XData Bar
{
}

}

Note that this gets XData blocks defined in a class; if you want to get "inherited" XData blocks listed for each subclass along with the inherited content it's only slightly more complex:

ClassMethod GetXDataContents(package As %String) As %Library.ArrayOfObjects
{
    $$$ThrowOnError($System.OBJ.GetPackageList(.classes,package))
    Set array = ##class(%Library.ArrayOfObjects).%New()
    Set class = ""
    For {
        Set class = $Order(classes(class))
        Quit:class=""
        Set xDataName = ""
        For {
            Set xDataName = $$$comMemberNext(class,$$$cCLASSxdata,xDataName)
            Quit:xDataName=""
            Set origin = $$$comMemberKeyGet(class,$$$cCLASSxdata,xDataName,$$$cXDATAorigin)
            Set xdata = ##class(%Dictionary.XDataDefinition).IDKEYOpen(origin,xDataName,,.sc)
            $$$ThrowOnError(sc)
            Do array.SetAt(xdata.Data,class_":"_xDataName)
        }
    }
    Quit array
}

Great answer, but I'd like to add that there are two distinct cases for working with XDatas:

  1. Where XData itself is an object of work (for example generating a part of WSDL spec and saving it in a class XData).
  2. Where XData is just a container for miscellaneous data (for example a template with placeholders).

The code above works for the first case, but for the second case it might be preferable to create an independent copy of an XData stream so that no locking happens - this prevents XData object access/modification errors, especially in Dev environments. Furthermore objectless way of getting XData contents would be faster.

I usually use this method to get streams if my XData work falls into the second category:

ClassMethod getClassXData(className, xdataName) As %Stream.Object
{
    set stream = ##class(%Stream.TmpCharacter).%New()
    for i=1:1:$$$comMemberKeyGet(className,$$$cCLASSxdata,xdataName,$$$cXDATAdata) {
        do stream.WriteLine($$$comMemberArrayGet(className,$$$cCLASSxdata,xdataName,$$$cXDATAdata,i))
    }
    quit stream
}

This code can be further improved for most use cases by replacing a stream with a string.