Groovy:使用JAX-B对象的特定属性创建一个Map

我有一个示例LEDES XML文件https://codebeautify.org/xmlviewer/cbdc79e7

使用JDK的xjc生成Ledesxmlebilling21类,如下所示和Ledes21.xsd模式https://codebeautify.org/xmlviewer/cb974a2e

 xjc -d src ledes21.xsd 

我正在使用JAX-B将XML转换为Java对象,如下所示

 Ledesxmlebilling21 XMLtoObject(InputStream fis) throws Exception { JAXBContext context = JAXBContext.newInstance(Ledesxmlebilling21.class) Unmarshaller um = context.createUnmarshaller() Ledesxmlebilling21 ledes = (Ledesxmlebilling21) um.unmarshal(fis) return ledes } 

我正在尝试使用Invoice对象的invId属性值创建一个Map作为Key,并将值作为所有Invoice对象的嵌套属性的fileItemNbr值列表,如下所示

 ['Invoice 31' : [10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33] 'Invoice 32' : [50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73] ] 

有人可以帮我吗?

更新解决方案

 def extractFileItemNbr(input, List extracted) { input.properties.each { prop, val -> //LedesXmlRuleProcessor.groovy:82) if (prop in ["metaClass", "class"]) return if (prop == 'file_item_nbr') { extracted << val } else { extractFileItemNbr(val, extracted) //LedesXmlRuleProcessor.groovy:87) } } } def extractFileItemNbr(List input, List extracted) { input.each { extractFileItemNbr(it, extracted) } } void testExtract(Ledesxmlebilling21 ledesxmlebilling21) { def xmlInvoices = ledesxmlebilling21.firm.client.invoice.flatten() Map<String, List> extracted = [:] println "invoices -- "+xmlInvoices for (Invoice invoice : xmlInvoices) { def accuList = [] extractFileItemNbr(invoice, accuList) extracted.put(invoice.invId, accuList) } println("extracted file_item_nbr "+ extracted) } 

我正在使用实际的Ledesxmlebilling21对象获得以下exception

 Disconnected from the target VM, address: '127.0.0.1:59759', transport: 'socket' 2017-12-11 11:04:06 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause java.lang.StackOverflowError: null at org.codehaus.groovy.util.AbstractConcurrentMap.getOrPut(AbstractConcurrentMap.java:37) at org.codehaus.groovy.reflection.GroovyClassValuePreJava7.get(GroovyClassValuePreJava7.java:94) at org.codehaus.groovy.reflection.ClassInfo.getClassInfo(ClassInfo.java:143) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:265) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:879) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.createPojoMetaClassGetPropertySite(AbstractCallSite.java:351) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.createGetPropertySite(AbstractCallSite.java:327) at org.codehaus.groovy.runtime.callsite.GetEffectivePojoPropertySite.acceptGetProperty(GetEffectivePojoPropertySite.java:56) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:296) at com.validation.rule.processor.impl.LedesXmlRuleProcessor.extractFileItemNbr(LedesXmlRuleProcessor.groovy:82) at sun.reflect.GeneratedMethodAccessor82.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:174) at com.validation.rule.processor.impl.LedesXmlRuleProcessor$_extractFileItemNbr_closure2.doCall(LedesXmlRuleProcessor.groovy:87) 

我的猜测是架构将具有循环属性。 或许在这看一下: JAXB映射对XML的循环引用

我相信你想要的是递归迭代groovy属性 。

我跳过了JAX-B解析,因为你已经解决了,并使用我自己的类。 groovy代码不是惯用的,可以缩短

 class LedesStatementTest extends GroovyTestCase { // Recursive function adding file_item_nbr to given list def extractFileItemNbr(input, List extracted) { input.properties.each { prop, val -> if (prop in ["metaClass", "class"]) return if (prop == 'file_item_nbr') { // println(" $prop : $val") extracted << val } else { extractFileItemNbr(val, extracted) } } } // deal with list fields def extractFileItemNbr(List input, List extracted) { input.each { extractFileItemNbr(it, extracted) } } void testExtract() { List invoices = [new LedesInvoice([inv_id: 'Invoice 31', file_item_nbr: 10, statement: new LedesStatement([file_item_nbr: 11]), summary: [new LedesTaxSummary([file_item_nbr: 12]), new LedesTaxSummary([file_item_nbr: 13])]]), new LedesInvoice([inv_id: 'Invoice 32', file_item_nbr: 50, statement: new LedesStatement([file_item_nbr: 51]), summary: [new LedesTaxSummary([file_item_nbr: 52]), new LedesTaxSummary([file_item_nbr: 53])]]) ] Map> extracted = [:] for (LedesInvoice invoice : invoices) { def accuList = [] extractFileItemNbr(invoice, accuList) extracted.put(invoice.inv_id, accuList) } println(extracted) } // data classes, similar to Ledes XML, simplified static class LedesInvoice { String inv_id; int file_item_nbr; LedesStatement statement; List summary; } static class LedesStatement { int file_item_nbr; } static class LedesTaxSummary { int file_item_nbr; } } 

输出:

 [Invoice 31:[12, 13, 11, 10], Invoice 32:[52, 53, 51, 50]] 

更新:

在循环的情况下,不要只传递List extracted的int List extractedList extracted ,还要传递一组访问的输入,并在每个提取方法中检查给定的输入是否已经在列表中。