Question
· May 29, 2018

Generated xml encountered a problem

I want to generate one of the following xml data, but after I generate only de="", after I need to generate is de="DEX71.41.009.01"

//// result:
<OPTREGISTER>
      <THBZ de="DEX71.41.007.01" display=""></THBZ>
      <GHFS de="DEX71.41.008.01" display=""></GHFS>
      <GHF de="DEX71.41.009.01"></GHF>
      <ZLF de="DEX71.41.009.01"></ZLF>
      <QTF de="DEX71.41.009.01"></QTF>
      <WDBZ de="DEX71.41.010.01" display=""></WDBZ>
      <GHKSDM de="DE08.10.025.00" display=""></GHKSDM>
</OPTREGISTER>

The classes I use are as follows:

////// ------ Test.Common.PropertyParameters cls
Class Test.Common.PropertyParameters Extends %XML.PropertyParameters [ ProcedureBlock ]
{


Parameter HDSD As STRING;


Parameter DE As STRING;

}


//////// ------ Test.SendExternalModel.DE1
Class Test.SendExternalModel.DE1 Extends (%RegisteredObject, %XML.Adaptor) [ Inheritance = right, Not ProcedureBlock, PropertyClass = Test.Common.PropertyParameters ]
{

Parameter XMLIGNOREINVALIDTAG = 1;

Parameter XMLIGNORENULL = 1;

Parameter XMLNAME = "DE1";

Property DE As %String(XMLNAME = "de", XMLPROJECTION = "ATTRIBUTE");

Property Value As %String(XMLPROJECTION = "CONTENT");

}

//////// ------ Test.SendExternalModel.DE2
Class CQZXRIETSInterface.SendExternalModel.DE2 Extends (%RegisteredObject, %XML.Adaptor, DE1) [ Inheritance = right, Not ProcedureBlock, PropertyClass = Test.Common.PropertyParameters ]
{

Parameter XMLIGNOREINVALIDTAG = 1;

Parameter XMLIGNORENULL = 1;

Parameter XMLNAME = "DE2";

Property Display As %String(XMLNAME = "display", XMLPROJECTION = "ATTRIBUTE");

}


////// ------ Test.Model.OPTREGISTER cls

Class Test.Model.OPTREGISTER Extends (%RegisteredObject, %XML.Adaptor) [ PropertyClass = Test.Common.PropertyParameters ]
{

Parameter XMLIGNOREINVALIDTAG = 1;

Parameter XMLIGNORENULL = 1;

Parameter XMLNAME = "OPTREGISTER";

Property THBZ As Test.SendExternalModel.DE2(DE = "DEX71.41.007.01", DICLookUp = "Y", XMLNAME = "THBZ") [ Required ];


Property GHFS As Test.SendExternalModel.DE2(DE = "DEX71.41.008.01", DICLookUp = "Y", XMLNAME = "GHFS") [ Required ];


Property GHF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "GHF") [ Required ];


Property ZLF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "ZLF") [ Required ];


Property QTF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "QTF") [ Required ];


Property WDBZ As Test.SendExternalModel.DE1(DE = "DEX71.41.010.01", DICLookUp = "Y", XMLNAME = "WDBZ");


Property GHKSDM As Test.SendExternalModel.DE1(DE = "DE08.10.025.00", DICLookUp = "Y", XMLNAME = "GHKSDM") [ Required ];

}
 

I use ensemble 2016, thank you for your help!

Discussion (2)1
Log in or sign up to continue

The key point here is how to get the value of the "DE" property parameter from the OPTREGISTER class definition into the "DE" property of the corresponding property. The naming does not do that automatically. Generally, property parameters (like DE, DICLookUp, XMLNAME) are used for code generation, either in methods (the third example below), projections (like XML), or for validation of datatypes (e.g., %String MINLEN/MAXLEN).

Assuming XML export is done with (for example):

Set = ##class(Test.SendExternalModel.OPTREGISTER).%New()
Do o.XMLExport(,",literal,indent")

There are at least three options. I'd recommend the third, but the first two are perhaps easier to understand.

1) In a method of OPTREGISTER (perhaps %OnNew), instantiate the various properties and set the DE property of each of them. In this case, the DE property parameter is no longer needed. For example:


Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
  Set ..THBZ = ##class(Test.SendExternalModel.DE2).%New()
  Set ..THBZ.DE = "DEX71.41.007.01"
  Set ..GHFS = ##class(Test.SendExternalModel.DE2).%New()
  Set ..GHFS.DE = "DEX71.41.008.01"
  Set ..GHF = ##class(Test.SendExternalModel.DE1).%New()
  Set ..GHF.DE = "DEX71.41.009.01"
  Set ..ZLF = ##class(Test.SendExternalModel.DE1).%New()
  Set ..ZLF.DE = "DEX71.41.009.01"
  Set ..QTF = ##class(Test.SendExternalModel.DE1).%New()
  Set ..QTF.DE = "DEX71.41.009.01"
  Set ..WDBZ = ##class(Test.SendExternalModel.DE2).%New()
  Set ..WDBZ.DE = "DEX71.41.010.01"
  Set ..GHKSDM = ##class(Test.SendExternalModel.DE2).%New()
  Set ..GHKSDM.DE = "DE08.10.025.00"
  Quit $$$OK
}

2) Have DE1/DE2 extend %SerialObject rather than %RegisteredObject, and define a constructor in DE1. (DE2 can have inheritance simplified a bit, although perhaps not in the context of your full application.) Then, define properties in OPTREGISTER with an InitialExpression - this is passed to the constructor for the serial object. This approach may not actually make sense in the context of your full application.

PropertyParameters:

Class Test.Common.PropertyParameters Extends %XML.PropertyParameters [ ProcedureBlock ]
{

Parameter HDSD As STRING;

Parameter DE As STRING;

Parameter DICLookUp As STRING;

}

DE1:

Class Test.SendExternalModel.DE1 Extends (%SerialObject, %XML.Adaptor) [ PropertyClass = Test.Common.PropertyParameters ]
{

Parameter XMLIGNOREINVALIDTAG = 1;

Parameter XMLIGNORENULL = 1;

Property DE As %String(XMLNAME = "de", XMLPROJECTION = "ATTRIBUTE");

Method %OnNew(pDE As %String = "") As %Status [ Private, ServerOnly = 1 ]
{
  Set ..DE = pDE
  Quit $$$OK
}

Storage Default
{
<Data name="DE1State">
<Value name="1">
<Value>DE</Value>
</Value>
</Data>
<State>DE1State</State>
<StreamLocation>^Test.SendExternalModel.DE1S</StreamLocation>
<Type>%Library.CacheSerialState</Type>
}

}

DE2:

Class Test.SendExternalModel.DE2 Extends Test.SendExternalModel.DE1
{

Property Display As %String(XMLNAME = "display", XMLPROJECTION = "ATTRIBUTE");

Storage Default
{
<Data name="DE2State">
<Value name="1">
<Value>DE</Value>
</Value>
<Value name="2">
<Value>Display</Value>
</Value>
</Data>
<State>DE2State</State>
<StreamLocation>^Test.SendExternalModel.DE2S</StreamLocation>
<Type>%Library.CacheSerialState</Type>
}

}

OPTREGISTER:

Class Test.SendExternalModel.OPTREGISTER Extends (%RegisteredObject, %XML.Adaptor) [ PropertyClass = Test.Common.PropertyParameters ]
{

Parameter XMLIGNOREINVALIDTAG = 1;

Parameter XMLIGNORENULL = 1;

Parameter XMLNAME = "OPTREGISTER";

Property THBZ As Test.SendExternalModel.DE2(DE = "DEX71.41.007.01", DICLookUp = "Y", XMLNAME = "THBZ") [ InitialExpression = "DEX71.41.007.01", Required ];

Property GHFS As Test.SendExternalModel.DE2(DE = "DEX71.41.008.01", DICLookUp = "Y", XMLNAME = "GHFS") [ InitialExpression = "DEX71.41.008.01", Required ];

Property GHF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "GHF") [ InitialExpression = "DEX71.41.009.01", Required ];

Property ZLF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "ZLF") [ InitialExpression = "DEX71.41.009.01", Required ];

Property QTF As Test.SendExternalModel.DE1(DE = "DEX71.41.009.01", XMLNAME = "QTF") [ InitialExpression = "DEX71.41.009.01", Required ];

Property WDBZ As Test.SendExternalModel.DE2(DE = "DEX71.41.010.01", DICLookUp = "Y", XMLNAME = "WDBZ") [ InitialExpression = "DEX71.41.010.01" ];

Property GHKSDM As Test.SendExternalModel.DE2(DE = "DE08.10.025.00", DICLookUp = "Y", XMLNAME = "GHKSDM") [ InitialExpression = "DE08.10.025.00", Required ];

}

3) Given all of your original class definitions, add a generator method in OPTREGISTER (or a parent class) that sets the "DE" property of any properties of type DE1/DE2 (or perhaps any properties that have your custom property parameters class) based on the DE property parameter in the containing class's definition. Ensure this method is called prior to XML export. (Might be useful if you need more fine-grained control - i.e., omitting the WDBZ element if it has no content.) This would be a great approach if you have lots of similar classes like these, to avoid repeating (1) every time. A really simple implementation (without any extra sanity checking for property types, etc.), equivalent to the first example, would look like:

Method %OnNew() As %Status [ CodeMode = objectgenerator, Private, ServerOnly = 1 ]
{
  Set tKey = ""
  For {
    #dim tProperty As %Dictionary.CompiledProperty
    Set tProperty = %compiledclass.Properties.GetNext(.tKey)
    If (tKey = "") {
      Quit
    }
    Set tDE = tProperty.Parameters.GetAt("DE")
    If (tDE '= "") {
      Do %code.WriteLine(" Set .."_$$$QN(tProperty.Name)_" = ##class("_tProperty.Type_").%New()")
      Do %code.WriteLine(" Set .."_$$$QN(tProperty.Name)_".DE = "_$$$QUOTE(tDE))
    }
  }
  Do %code.WriteLine(" Quit $$$OK")
  Quit $$$OK
}