xmlgrailsmarkupbuilder

Create XML based on class hierarchy


My code is as follows:

class GlobalInfo {
    // multiple properties here
    String name;
    GlobalInfo parent;
    static hasMany = [children: GlobalInfo]
}

class RegionalInfo extends GlobalInfo {
    // multiple properties here
}

class BankInfo extends GlobalInfo {
    // multiple properties here
}

There could be multiple BankInfo in one RegionalInfo. And there could be multiple RegionalInfo parents inside a BankInfo object.

For e.g.,

BankInfo == Bank Of America

RegionalInfo == Dallas // 1st parent

RegionalInfo == Texas // Dallas's parent

RegionalInfo == MidWest // Texas's parent and final parent.

Per above example, the immediate parent of BankInfo object points to Dallas, and it's parent points to Texas and then it's parent point to MidWest.

In some cases, there maybe only one or two parents

For e.g.,

BankInfo == University Credit Union

RegionalInfo == Houston

For e.g., a class heirarchy view from top most parent will look something like below. This is just an example and i could get any combination.

<MidWest>
   <Independent Bank Of MidWest/>
   <Midland Bank Of America/>
   <Texas>
      <Bank Of America>
      <Austin>
         <Supereme National Bank>
         <Master Chanse Bank>
         <Austin East>
            <National Bank Of Austin>
            <Federal Bank>
         </Austin East>
      </Austin>
   </Texas>
   <Kansas>
      <Federal Credit Union>
      <Kansas State Bank>
   </Kansas>
</MidWest>

The places are objects of RegionalInfo and bank's are objects of BankInfo.

The challenge here is, as you see there's a possibility of more RegionalInfo objects inside one RegionalInfo object.

Below is the code i came up with.

def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.root(name: "MidWest"){
    bank(name: "Independent Bank Of MidWest")
    bank(name: "Midland Bank Of America")
    location(name: "Texas", expanded: true){
        reader(name:"Bank Of America")
        location(name: "Austin", expanded: true){
            bank(name:"Supereme National Bank")
            bank(name:"Master Chanse Bank")
                location(name: "Austin East", expanded: true){
                    bank(name:"National Bank Of Austin")
                    bank(name:"Federal Bank")
                }
        }
    }
    location(name: "Kansas", expanded: true){
        bank(name:"Federal Credit Union")
        bank(name:"Kansas State Bank")
    }
}

The problem here is that everything is created statically. I want to create the XML dynamically, because the class object structure will change everytime.

Is it possible to do that?

The purpose of creating this XML is to use that in TreeView plugin to draw a class hierarchy structure from top most parent. In above example, the top most parent is MidWest.

UPDATED

XML generated for code above:

<root name='MidWest'>
  <bank name='Independent Bank Of MidWest' />
  <bank name='Midland Bank Of America' />
  <location name='Texas' expanded='true'>
    <bank name='Bank Of America' />
    <location name='Austin' expanded='true'>
      <bank name='Supereme National Bank' />
      <bank name='Master Chanse Bank' />
      <location name='Austin East' expanded='true'>
        <bank name='National Bank Of Austin' />
        <bank name='Federal Bank' />
      </location>
    </location>
  </location>
  <location name='Kansas' expanded='true'>
    <bank name='Federal Credit Union' />
    <bank name='Kansas State Bank' />
  </location>
</root>

Solution

  • Something like this (I adapted the classes to be able to run them standalone, by removing hasMany)?

    import groovy.xml.MarkupBuilder
    
    class GlobalInfo {
        String name
        GlobalInfo parent
        List<GlobalInfo> children
    }
    
    class RegionalInfo extends GlobalInfo {
        // multiple properties here
    
    }
    
    class BankInfo extends GlobalInfo {
        // multiple properties here
    
    }
    
    
    String root(GlobalInfo root) {
        def out = new StringWriter()
        MarkupBuilder xml = new MarkupBuilder(out)
        xml.root(name: root.name) {
            root.children.each {
                child(xml, it)
            }
        }
        out.toString()
    }
    
    
    void child(MarkupBuilder xml, RegionalInfo info) {
        xml.location(makeAttributes(info)) {
            info.children?.each { child(xml, it) }
        }
    }
    
    void child(MarkupBuilder xml, GlobalInfo info) {
        throw new RuntimeException("GlobalInfo not expected here")
    }
    
    void child(MarkupBuilder xml, BankInfo info) {
        xml.bank(makeAttributes(info))
    }
    
    protected LinkedHashMap<String, String> makeAttributes(GlobalInfo info) {
        def attrs = [
                name: info.name
        ]
        if (info.children) {
            attrs.expanded = true
        }
        attrs
    }
    
    def midwest = new GlobalInfo(
            name: "MidWest",
            children: [
                    new BankInfo(
                            name: 'Independent Bank Of MidWest'
                    ),
                    new RegionalInfo(
                            name: 'Texas',
                            children: [
                                    new BankInfo(
                                            name: 'Bank Of America'
                                    ),
                                    new RegionalInfo(
                                            name: 'Austin',
                                            children: [
                                                    new BankInfo(
                                                            name: 'National Bank Of Austin'
                                                    )
                                            ]
                                    )
                            ]
                    )
            ]
    )
    
    println root(midwest)
    

    resulting in:

    <root name='MidWest'>
      <bank name='Independent Bank Of MidWest' />
      <location name='Texas' expanded='true'>
        <bank name='Bank Of America' />
        <location name='Austin' expanded='true'>
          <bank name='National Bank Of Austin' />
        </location>
      </location>
    </root>