xmloopnamespacesapldyalog

Dyalog APL: How to write array of Objects (with NameSpace) to XML File?


In addition to the question Dyalog APL: How to write array of Objects to XML File?, I'd like to clarify this situation:

And if one field contains a namespace, then what will the XmlMat function look like? For example, let's add the Phone field (←⎕NS⍬) to the People class. And assign the value to Peoples[2].Phone.Work ← '12131'

:Class People
:Field Public Type ← 'contact'
:Field Public Name ← ⍬
:Field Public Company ← ⍬
:Field Public Position ← ⍬
:Field Public Fax ← ⍬
:Field Public Email ← ⍬
:Field Public Phone ← ⎕NS⍬

 ∇ make args
   :Access Public
   :Implements Constructor
   (Name Company Position)←args
   ⎕←'New people maked'
 ∇

:EndClass

 Peoples ← (⎕New People ('Name1' 'Company1' 'Founder')) (⎕New People ('Name2' 'Company2' 'Founder'))
New people maked
New people maked
 Peoples
#.[People]  #.[People]
Peoples[2].Phone.Work ← '12124523'
Peoples[2].Phone.Home ← '4323325'

Solution

  • Firstly, :Field Public Phone ← ⎕NS⍬ doesn't actually create one namespace per instance, as (per the documentation) the default value is established when the class is created and only copied to each instance, so they will each have a reference to the same namespace. Instead, we have to create the namespace in the constructor:

    ⋮
        :Field Public Phone
    
        ∇ make args
          :Access Public
          :Implements Constructor
          (Name Company Position)←args
          Phone←⎕NS ⍬
          ⎕←'New people maked'
        ∇
    ⋮
    

    We also need to create this namespace in the niladic constructor, and both need to print a message, so we let the monadic one call the niladic one:

        ∇ make0
          :Access Public
          :Implements Constructor
          Phone←⎕NS⍬
          ⎕←'New people maked'
        ∇
    
        ∇ make args
          :Access Public
          :Implements Constructor
          (Name Company Position)←args
          make0
        ∇
    

    Now we are ready to amend XmlMat to detect namespace values and do a recursive call. We also need it to detect when it is being called on this instance itself, so it can grab only fields and not other visible variables. Finally, we increment the XML nesting level for each nested namespace:

        ∇ nvm←name XmlMat ref;name;val
          :Access public
          nvm←⍉⍪1 name ''
          :For name :In ref.⎕NL -2+0.2×⎕THIS=ref
              val←ref⍎name
              :If 9=40 ⎕ATX'val'
                  nvm⍪←1+@1⍤1⊢name XmlMat val
              :Else
                  nvm⍪←2 name(⍕val)
              :EndIf
          :EndFor
        ∇
    

    Since XmlMat now needs the name of and reference to the target object, we amend ToXML accordingly:

     ToXML←{⎕XML 0 'Peoples' ''⍪⊃⍪⌿(⊂'People')⍵.XmlMat ⍵}
    

    Here's a trial run:

          Peoples ← (⎕New People ('Name1' 'Company1' 'Founder')) (⎕New People ('Name2' 'Company2' 'Founder'))
    New people maked
    New people maked
          Peoples[2].Phone.Work ← '12124523'
          Peoples[2].Phone.Home ← '4323325'
          ToXML Peoples
    <Peoples>                       
      <People>                      
        <Company>Company1</Company> 
        <Email></Email>             
        <Fax></Fax>                 
        <Name>Name1</Name>          
        <Phone></Phone>             
        <Position>Founder</Position>
        <Type>contact</Type>        
      </People>                     
      <People>                      
        <Company>Company2</Company> 
        <Email></Email>             
        <Fax></Fax>                 
        <Name>Name2</Name>          
        <Phone>                     
          <Home>4323325</Home>      
          <Work>12124523</Work>     
        </Phone>                    
        <Position>Founder</Position>
        <Type>contact</Type>        
      </People>                     
    </Peoples>                      
    

    You will of course have to amend FromXML accordingly.