xmlfileoopapldyalog

Dyalog APL: How to write array of Objects to XML File?


I have Object class like that:

:Class People     
  :Field Public Type ← 'contact'
  :Field Public Name ← ⍬   
  :Field Public Company ← ⍬         
  :Field Public Position ← ⍬
  :Field Public Fax ← ⍬
  :Field Public Email ← ⍬
   
  ∇ make args
    :Access Public 
    :Implements Constructor
    Name ← args[1]
    Company ← args[2]
    Position ← args[3]
    ⎕ ← 'New people maked'
  ∇

:EndClass

And I have an array of Objects of People class

  Peoples ← (⎕New People ('Name1' 'Company1' 'Founder')) (⎕New People ('Name2' 'Company2' 'Founder'))
New people maked
New people maked
  Peoples
#.[People]  #.[People]

How do I export Peoples to an xml file? And then import an array from xml?

The task is to save the array and load it from the file when you restart it.


Solution

  • Look into the system function ⎕XML and decide on a way to represent your fields as XML.

    You may bump into a problem with your field values being nested scalars. This is because you use args[1] etc. instead of 1⊃args etc. or simply (Name Company Position)←args.

    With the nesting problem resolved, we can add a method to generate part of the matrix that ⎕XML needs. I've decided on a simple schema here, but you can make it whatever you want:

    :Class People
        :Field Public Type ← 'contact'
        :Field Public Name ← ⍬
        :Field Public Company ← ⍬
        :Field Public Position ← ⍬
        :Field Public Fax ← ⍬
        :Field Public Email ← ⍬
    
        ∇ make args
          :Access Public
          :Implements Constructor
          (Name Company Position)←args
          ⎕←'New people maked'
        ∇
    
        ∇ nvm←XmlMat
          :Access public
          nvm←1 'People' ''⍪↑{2 ⍵(⍕⍎⍵)}¨⎕NL ¯2
        ∇
    
    :EndClass
    
            Peoples.XmlMat
    ┌─────────────────────┬─────────────────────┐
    │┌─┬────────┬────────┐│┌─┬────────┬────────┐│
    ││1│People  │        │││1│People  │        ││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Company │Company1│││2│Company │Company2││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Email   │        │││2│Email   │        ││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Fax     │        │││2│Fax     │        ││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Name    │Name1   │││2│Name    │Name2   ││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Position│Founder │││2│Position│Founder ││
    │├─┼────────┼────────┤│├─┼────────┼────────┤│
    ││2│Type    │contact │││2│Type    │contact ││
    │└─┴────────┴────────┘│└─┴────────┴────────┘│
    └─────────────────────┴─────────────────────┘
    

    Here's then a function that takes a vector of Peoples and constructs the final argument to supply ⎕XML:

     ToXML←{⎕XML 0 'Peoples' ''⍪⊃⍪⌿⍵.XmlMat}
    
          ToXML Peoples
    <Peoples>                       
      <People>                      
        <Company>Company1</Company> 
        <Email></Email>             
        <Fax></Fax>                 
        <Name>Name1</Name>          
        <Position>Founder</Position>
        <Type>contact</Type>        
      </People>                     
      <People>                      
        <Company>Company2</Company> 
        <Email></Email>             
        <Fax></Fax>                 
        <Name>Name2</Name>          
        <Position>Founder</Position>
        <Type>contact</Type>        
      </People>                     
    </Peoples>                      
    

    Writing the XML to a file is a simple as:

    'peoples.xml'⎕NPUT⍨ToXML Peoples
    

    Next, we want to load the XML and create People objects from it. Getting the XML content into matrix form is easy (output abbreviated for brevity):

          ⎕XML⊃⎕NGET'peoples.xml'
    ┌─┬────────┬────────┬───┬─┐
    │0│Peoples │        │   │3│
    ├─┼────────┼────────┼───┼─┤
    │1│People  │        │   │3│
    ├─┼────────┼────────┼───┼─┤
    │2│Company │Company1│   │5│
    ├─┼────────┼────────┼───┼─┤
    : :        :        :   : :
    

    We're only interested in the first three columns, which are XML nesting level, name, and value:

    (level name value)←↓3↑⍉⎕XML⊃⎕NGET'peoples.xml'
    

    We can now partition the names and values that belong together:

          name⊆⍨2=level
    ┌──────────────────────────────────────┬──────────────────────────────────────┐
    │┌───────┬─────┬───┬────┬────────┬────┐│┌───────┬─────┬───┬────┬────────┬────┐│
    ││Company│Email│Fax│Name│Position│Type│││Company│Email│Fax│Name│Position│Type││
    │└───────┴─────┴───┴────┴────────┴────┘│└───────┴─────┴───┴────┴────────┴────┘│
    └──────────────────────────────────────┴──────────────────────────────────────┘
          value⊆⍨2=level
    ┌──────────────────────────────────┬──────────────────────────────────┐
    │┌────────┬┬┬─────┬───────┬───────┐│┌────────┬┬┬─────┬───────┬───────┐│
    ││Company1│││Name1│Founder│contact│││Company2│││Name2│Founder│contact││
    │└────────┴┴┴─────┴───────┴───────┘│└────────┴┴┴─────┴───────┴───────┘│
    └──────────────────────────────────┴──────────────────────────────────┘
    

    Let's amend the class with a niladic constructor so we can set the field values after construction:

    :Class People
        :Field Public Type ← 'contact'
        :Field Public Name ← ⍬
        :Field Public Company ← ⍬
        :Field Public Position ← ⍬
        :Field Public Fax ← ⍬
        :Field Public Email ← ⍬
    
        ∇ make0
          :Access Public
          :Implements Constructor
          ⎕←'New people maked'
        ∇
    
        ∇ make args
          :Access Public
          :Implements Constructor
          (Name Company Position)←args
          ⎕←'New people maked'
        ∇
    
        ∇ nvm←XmlMat
          :Access public
          nvm←1 'People' ''⍪↑{2 ⍵(⍕⍎⍵)}¨⎕NL ¯2
        ∇
    
    :EndClass
    

    And finally, we can create our FromXML function based on the above experiments:

     FromXML←{
         (level name value)←↓3↑⍉⎕XML ⍵
         (names values)←name value⊆¨⍨⊂2=level
         names{p⊣(p←⎕NEW People)⍎(∊' ',¨⍺),'←⍵'}¨values
     }
    
          FromXML⊃⎕NGET'peoples.xml'
    New people maked
    New people maked
     #.[People]  #.[People]