使用MarkupBuilder或StreamingMarkupBuilder生成嵌套XML

时间:2013-01-08 01:50:00

标签: groovy xml-serialization markupbuilder

我有一个分组查询,会产生一个诊所列表。诊所内有患者。并且患者内有处方。我正在尝试使用MarkupBuilder输出这个结构,但我似乎无法使收容工作。

我得到的是:

<worklist>
   <clinics>
      <clinic id="1" name="Clinic 1"/>
      <patient firstName="John" id="2" lastName="Doe"/>
      <prescription id="4">
         <prescriptionType/>
         <duration/>
         <drugName>Tums</drugName>
         <route/>
         <refills>0</refills>
      </prescription>
      <clinic id="2" name="Clinic 2"/>
      <patient firstName="John" id="2" lastName="Doe"/>
      <prescription id="2">
         <prescriptionType>Formulary</prescriptionType>
         <duration>duration</duration>
         <drugName>Lipitor</drugName>
         <route>route</route>
         <refills>5</refills>
      </prescription>
      <patient firstName="Sylvia" id="4" lastName="Plath"/>
      <prescription id="5">
         <prescriptionType/>
         <duration/>
         <drugName>BandAids</drugName>
         <route/>
         <refills>0</refills>
      </prescription>
   </clinics>
</worklist>

请注意,诊所元素关闭,不包含患者。并且患者元素关闭并且不包含处方。这是不正确的。它应该是这样的:

<worklist>
   <clinics>
      <clinic id="1" name="Clinic 1">
          <patient firstName="John" id="2" lastName="Doe">
              <prescription id="4">
                 <prescriptionType/>
                 <duration/>
                 <drugName>Tums</drugName>
                 <route/>
                 <refills>0</refills>
              </prescription>
          </patient>
      </clinic>   
      <clinic id="2" name="Clinic 2"/>
          <patient firstName="John" id="2" lastName="Doe">
              <prescription id="2">
                 <prescriptionType>Formulary</prescriptionType>
                 <duration>duration</duration>
                 <drugName>Lipitor</drugName>
                 <route>route</route>
                 <refills>5</refills>
              </prescription>
          </patient>
          <patient firstName="Sylvia" id="4" lastName="Plath">
              <prescription id="5">
                 <prescriptionType/>
                 <duration/>
                 <drugName>BandAids</drugName>
                 <route/>
                 <refills>0</refills>
              </prescription>
          </patient>
      </clinic>   
   </clinics>
</worklist>

这是我的代码:

    import groovy.xml.StreamingMarkupBuilder
    import groovy.xml.XmlUtil

    import javax.ws.rs.GET
    import javax.ws.rs.Path
    import javax.ws.rs.Produces

    @Path('/api/worklist')
    class WorklistResource {
        def addClinic = { idx, name ->
            clinic(id:idx, name:name)
        }

        def addPatient = { idx, fname, lname ->
            patient(id:idx, firstName:fname, lastName:lname)
        }

        @GET
        @Produces(['application/xml','application/json'])
        String getWorklistRepresentation() {
            def groupedScripts = Prescription.createCriteria().list {
                createAlias('clinic', 'clinicAlias')
                createAlias('patient', 'patientAlias')
                projections {
                    groupProperty "id"
                    groupProperty "clinicAlias.id"
                    groupProperty "patientAlias.id"
                }
                order "clinicAlias.name"
                order "patientAlias.lastName"
                order "patientAlias.firstName"
            }

            def curClinic = null
            def curPatient = null

            def worklist = new StreamingMarkupBuilder().bind {
                worklist {
                    clinics {
                        groupedScripts.each { arr ->
                            def (rx, clinic, patient) = arr
                            def script = Prescription.get(rx)
                            def cl = Clinic.get(clinic)
                            def pat = Patient.get(patient)

                            if( curClinic != cl ) {
                                curClinic = cl
                                addClinic.delegate = delegate
                                addClinic(cl.id, cl.name)
                            }

                            if( curPatient != pat ) {
                                curPatient = pat
                                addPatient.delegate = delegate
                                addPatient(pat.id, pat.firstName, pat.lastName)
                            }

                            prescription(id:script.id) {
                                prescriptionType(script.prescriptionType)
                                duration(script.duration)
                                drugName(script.drugName)
                                route(script.route)
                                refills(script.refills)
                            }
                        }
                    }
                }
            }

            def xml = XmlUtil.serialize(worklist)
            xml
        }
    }

显然,我需要以某种方式保持诊所关闭,直到我到新诊所或到达收集结束。患者关闭也一样。我只是不确定该怎么做。

提前感谢您的帮助。我需要今晚让这个工作。

2 个答案:

答案 0 :(得分:2)

您只能在列表中使用each方法。您必须注意以下事项:builder.bind内的每个匹配方法调用都将调用相应的方法/变量;因此你需要不同的名字。这甚至更好,因为你没有做出模棱两可的命名。您可以在eachs内构建整个XML。它有点大,但这会以你想要的方式填充你的XML:

<强>更新

由于你的模型是颠倒的,我做了以下几点:

// definition of the model and mock data
@Canonical class Prescription {
  int id
  String prescriptionType, duration, drugName, route
  int refills
  Clinic clinic
  Patient patient
}

@Canonical class Clinic {
  int id
  String name
}

@Canonical class Patient {
  int id
  String firstName, lastName
}


def patient2 = new Patient(2, "John", "Doe")
def patient4 = new Patient(4, "Sylvia", "Plath")

def clinic1 = new Clinic(1, "Clinic 1")
def clinic2 = new Clinic(2, "Clinic 2")

def prescriptions = [
  new Prescription(2, "Formulary", "duration", "Lipitor", "route", 5, clinic1, patient2),
  new Prescription(4, null, null, "Tums", null, 0, clinic2, patient2),
  new Prescription(5, null, null, "BandAids", null, 5, clinic2, patient4)
]

最好的办法是反转模型,使其与XML结构相匹配。您可以使用此代码段轻松撤消它:

clins = prescriptions.inject([:].withDefault({ [:] })) { map, pres -> 
  map[ pres.clinic ] << [ (pres.patient) : pres ]
  map
}

现在根据地图结构构建XML:

builder = new groovy.xml.StreamingMarkupBuilder()

xml = builder.bind { 

  clinics { 

    clins.each { cliEntry -> cli = cliEntry.key

      clinic(id: cli.id, name: cli.name) {

        cliEntry.value.each { patEntry -> pat = patEntry.key

          patient(id: pat.id, firstName: pat.firstName, lastName: pat.lastName){

            patEntry.value.each { pres ->

              prescription(id: pres.id) {
                prescriptionType pres.prescriptionType
                duration pres.duration
                drugName pres.drugName
                route pres.route
                refills pres.refills
              }
            }
          }
        }
      }
    }
  } 
}

测试:

assert xml.toString() == """<clinics><clinic id='1' name='Clinic 1'><patient id='2' firstName='John' lastName='Doe'><prescription id='2'><prescriptionType>Formulary</prescriptionType><duration>duration</duration><drugName>Lipitor</drugName><route>route</route><refills>5</refills></prescription></patient></clinic><clinic id='2' name='Clinic 2'><patient id='2' firstName='John' lastName='Doe'><prescription id='4'><prescriptionType/><duration/><drugName>Tums</drugName><route/><refills>0</refills></prescription></patient><patient id='4' firstName='Sylvia' lastName='Plath'><prescription id='4'><prescriptionType/><duration/><drugName>Tums</drugName><route/><refills>0</refills></prescription></patient></clinic></clinics>"""

答案 1 :(得分:0)

我在这里有一些丑陋的全局_index变量,终止条件和try..catch废话,但我已经在这里工作了好几个小时,这段代码有效:

    import groovy.xml.StreamingMarkupBuilder
    import groovy.xml.XmlUtil

    import javax.ws.rs.GET
    import javax.ws.rs.Path
    import javax.ws.rs.Produces

    @Path('/api/worklist')
    class WorklistResource {
        def _index = null   // global index into groupedScripts collection

        def addClinic = { scripts, index ->
            addPatient.delegate = delegate
            def (rxId, clinicId, patientId) = scripts[index]
            def startingClinicId = clinicId
            def cl = Clinic.get(clinicId)

            clinic(id:cl.id, name:cl.name) {
                while( clinicId == startingClinicId ) {
                    _index = index
                    index = addPatient(scripts, index)
                    if( index == -1 ) return -1
                    try {
                        (rxId, clinicId, patientId) = scripts[++index]
                    } catch(NullPointerException e) {
                        return -1
                    }
                }
            }
            return _index
        }

        def addPatient = { scripts, index ->
            def result = index
            def (rxId, clinicId, patientId) = scripts[index]
            def startingPatientId = patientId
            def pat = Patient.get(patientId)

            patient(id:pat.id, firstName:pat.firstName, lastName:pat.lastName) {
                while( patientId == startingPatientId ) {
                    _index = index
                    def script = Prescription.get(rxId)
                    prescription(id:script.id) {
                        prescriptionType(script.prescriptionType)
                        duration(script.duration)
                        drugName(script.drugName)
                        route(script.route)
                        refills(script.refills)
                    }
                    try {
                        (rxId, clinicId, patientId) = scripts[++index]
                    } catch(NullPointerException e) {
                        return -1
                    }
                }
            }
            return _index
        }

        @GET
        @Produces(['application/xml','application/json'])
        String getWorklistRepresentation() {
            def groupedScripts = Prescription.createCriteria().list {
                createAlias('clinic', 'clinicAlias')
                createAlias('patient', 'patientAlias')
                projections {
                    groupProperty "id"
                    groupProperty "clinicAlias.id"
                    groupProperty "patientAlias.id"
                }
                order "clinicAlias.name"
                order "patientAlias.lastName"
                order "patientAlias.firstName"
            }

            def finished = false
            def worklist = new StreamingMarkupBuilder().bind {
                worklist {
                    clinics {
                        _index = 0
                        addClinic.delegate = delegate

                        while( !finished && _index < groupedScripts.size() ) {
                            _index = addClinic(groupedScripts, _index)
                            if( _index == -1 ) finished = true
                            ++_index
                        }
                    }
                }
            }

            def xml = XmlUtil.serialize(worklist)
            xml
        }
    }

输出结果为:

    <worklist>
       <clinics>
          <clinic id="1" name="Clinic 1">
             <patient firstName="Mariah" id="3" lastName="Brookstone">
                <prescription id="1">
                   <prescriptionType>New</prescriptionType>
                   <duration>30 days</duration>
                   <drugName>Lisinopril 20mg Tablet</drugName>
                   <route>By Mouth</route>
                   <refills>2</refills>
                </prescription>
             </patient>
             <patient firstName="John" id="2" lastName="Doe">
                <prescription id="4">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Tums</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
          </clinic>
          <clinic id="2" name="Clinic 2">
             <patient firstName="Mariah" id="3" lastName="Brookstone">
                <prescription id="11">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Milk Duds</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
                <prescription id="12">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Hershey</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
                <prescription id="7">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Skittles</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
             <patient firstName="John" id="2" lastName="Doe">
                <prescription id="2">
                   <prescriptionType>Formulary</prescriptionType>
                   <duration>duration</duration>
                   <drugName>Lipitor</drugName>
                   <route>route</route>
                   <refills>5</refills>
                </prescription>
             </patient>
             <patient firstName="Sylvia" id="4" lastName="Plath">
                <prescription id="5">
                   <prescriptionType/>
                   <duration/>
                   <drugName>BandAids</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
          </clinic>
          <clinic id="3" name="Clinic 3">
             <patient firstName="Jane" id="12" lastName="Doe">
                <prescription id="9">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Reese's Pieces</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
             <patient firstName="Jill" id="13" lastName="Doe">
                <prescription id="8">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Toothpaste</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
             <patient firstName="Jim" id="11" lastName="Doe">
                <prescription id="10">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Hallmark</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
             <patient firstName="John" id="2" lastName="Doe">
                <prescription id="6">
                   <prescriptionType/>
                   <duration/>
                   <drugName>Gauze</drugName>
                   <route/>
                   <refills>0</refills>
                </prescription>
             </patient>
          </clinic>
       </clinics>
    </worklist>

那(几乎)正是我所追求的。