Groovy删除JSON匹配元素时遇ConcurrentModificationException问题求助
解决Groovy遍历JSON时删除元素引发的ConcurrentModificationException错误
问题场景
加载JSON文件并遍历元素,删除匹配指定值的元素后生成新JSON。当JSON仅含一个元素时可成功删除,但存在两个元素时触发java.util.ConcurrentModificationException错误。
现有sarva.json文件内容
{ "stack-0": { "name": "foo-web", "createdAt": "2022-11-30T10:56:32.551977633Z", "dbName": "foo-DB" }, "stack-1": { "name": "bar-web", "createdAt": "2022-11-30T10:56:32.551977633Z", "dbName": "bar-DB" } }
报错代码
import groovy.json.* def jsonSlurper = new JsonSlurper() def json = jsonSlurper.parse(new File("sarva.json")) json.each { key, val -> if ("$val.name" == "foo-web") { json.remove(key) println json } } // 更新sarva.json文件 newJson = new JsonBuilder(json).toPrettyString() new File("sarva.json").write(newJson)
报错信息
[stack-1:[createdAt:2022-11-30T10:56:32.551977633Z, dbName:bar-DB, name:bar-web]] java.util.ConcurrentModificationException at java.base/java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1208) at java.base/java.util.TreeMap$EntryIterator.next(TreeMap.java:1244) at java.base/java.util.TreeMap$EntryIterator.next(TreeMap.java:1239) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2201) at org.codehaus.groovy.runtime.dgm$164.invoke(Unknown Source) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128) at Script1.run(Script1.groovy:5) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:574) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:612) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:583) at hudson.util.RemotingDiagnostics$Script.call(RemotingDiagnostics.java:149) at hudson.util.RemotingDiagnostics$Script.call(RemotingDiagnostics.java:118) at hudson.remoting.LocalChannel.call(LocalChannel.java:47) at hudson.util.RemotingDiagnostics.executeGroovy(RemotingDiagnostics.java:115) at jenkins.model.Jenkins._doScript(Jenkins.java:4729) at jenkins.model.Jenkins.doScript(Jenkins.java:4700)
解决方案
原因分析
ConcurrentModificationException的触发原因是:在遍历集合(此处为TreeMap,由JsonSlurper解析生成)的同时,直接修改了集合的结构(执行remove操作),集合的迭代器会检测到这种并发修改并抛出异常。
方法一:遍历集合的副本
通过遍历原集合的keySet副本,避免在遍历过程中修改原集合的结构:
import groovy.json.* def jsonSlurper = new JsonSlurper() def json = jsonSlurper.parse(new File("sarva.json")) // 遍历keySet的副本,避免遍历原集合时修改结构触发异常 json.keySet().toList().each { key -> def val = json[key] if (val.name == "foo-web") { json.remove(key) println json } } def newJson = new JsonBuilder(json).toPrettyString() new File("sarva.json").write(newJson)
方法二:生成过滤后的新集合(推荐)
利用Groovy的findAll方法直接生成过滤后的新Map,无需修改原集合,代码更简洁且符合函数式编程风格:
import groovy.json.* def jsonSlurper = new JsonSlurper() def json = jsonSlurper.parse(new File("sarva.json")) // 过滤出name不等于"foo-web"的元素,生成新Map def filteredJson = json.findAll { key, val -> val.name != "foo-web" } println filteredJson def newJson = new JsonBuilder(filteredJson).toPrettyString() new File("sarva.json").write(newJson)
预期输出
执行上述代码后,控制台输出:
[stack-1:[createdAt:2022-11-30T10:56:32.551977633Z, dbName:bar-DB, name:bar-web]]
sarva.json文件仅保留stack-1的信息。
内容的提问来源于stack exchange,提问作者Sarva
相关产品推荐
相关产品推荐

