From 2857b0c4d7a7537e7212dd468a71401c25021357 Mon Sep 17 00:00:00 2001 From: huzhengkao <562572218@qq.com> Date: Fri, 30 May 2025 16:21:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 47 ++++++++ .../controller/ProvTestServerController.java | 17 ++- .../d2d/provtest/domain/DomesticProducer.java | 5 +- .../d2d/provtest/entity/VTTSBasic.java | 11 +- .../provtest/service/XmlParserService.java | 108 +++++++++++++++--- .../controller/ProvTestServerController.class | Bin 7569 -> 7501 bytes .../provtest/domain/DomesticProducer.class | Bin 15226 -> 15230 bytes .../entity/VTTSBasic$KnownTypes.class | Bin 587 -> 0 bytes .../d2d/provtest/entity/VTTSBasic.class | Bin 3212 -> 3314 bytes .../provtest/service/XmlParserService.class | Bin 2896 -> 7078 bytes .../compile/default-compile/createdFiles.lst | 27 +++++ 11 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 .gitignore delete mode 100644 target/classes/com/alihealth/d2d/provtest/entity/VTTSBasic$KnownTypes.class diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8368a --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java b/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java index f59ff6b..da29b5b 100644 --- a/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java +++ b/src/main/java/com/alihealth/d2d/provtest/controller/ProvTestServerController.java @@ -100,17 +100,16 @@ public class ProvTestServerController { // List list= XmlStringParser.parseVaccineManufacturers(xml,eventPojo.getSubType()); //解析并保存数据 -// XmlStringParser.parseXml(xml, "1011", data -> { -// domesticPharmaBaseInfoService.save((DomesticPharmaBaseInfo) data); -// }); + XmlStringParser.parseXml(xml, "1011", data -> { + domesticPharmaBaseInfoService.save((DomesticPharmaBaseInfo) data); + }); - List producers = xmlParserService.parseMultiDataXml( - xml, - VTTSBasic.class, - DomesticProducer.class - ); +// List producers = xmlParserService.parseMultiDataXml( +// xml, +// DomesticProducer.class +// ); - logger.info("解析后的数据条数:{}", producers.size()); + logger.info("解析后的数据条数:{}", ""); return ResponseBuilder.success(); }catch (Exception e){ diff --git a/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java index a6b0614..0664632 100644 --- a/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java +++ b/src/main/java/com/alihealth/d2d/provtest/domain/DomesticProducer.java @@ -131,7 +131,8 @@ public class DomesticProducer extends BaseEntity{ @Override public String getDatasetName() { - return "境内药品生产企业基本信息数据子集"; + return "境内药品生产企业基本信息数据子集1111"; } -} \ No newline at end of file + +} diff --git a/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java b/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java index 1e87ce7..dd430ec 100644 --- a/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java +++ b/src/main/java/com/alihealth/d2d/provtest/entity/VTTSBasic.java @@ -14,20 +14,17 @@ import java.util.List; @XmlRootElement(name = "VTTSBasic") @XmlAccessorType(XmlAccessType.FIELD) @Data +@XmlSeeAlso({DomesticProducer.class}) public class VTTSBasic { @XmlElement(name = "datasetName") private String datasetName; @XmlElementWrapper(name = "dataset") + //@XmlElements(@XmlElement(name = "data")) @XmlElement(name = "data") - private List dataset = new ArrayList<>(); // 必须初始化集合 + // private List dataset; // 必须初始化集合 + private List dataset; // 确保有无参构造器 public VTTSBasic() {} - - // 添加@XmlSeeAlso指定实现类 - @XmlSeeAlso({ - DomesticProducer.class - }) - public static class KnownTypes {} } diff --git a/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java b/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java index 3695b7c..bacdee8 100644 --- a/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java +++ b/src/main/java/com/alihealth/d2d/provtest/service/XmlParserService.java @@ -1,23 +1,24 @@ package com.alihealth.d2d.provtest.service; +import com.alihealth.d2d.provtest.domain.DomesticProducer; +import com.alihealth.d2d.provtest.entity.VTTSBasic; import org.springframework.stereotype.Service; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import javax.xml.XMLConstants; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Unmarshaller; +import javax.xml.bind.*; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * @author Jason @@ -26,35 +27,104 @@ import java.util.List; @Service public class XmlParserService { + private static final Map, JAXBContext> CONTEXT_CACHE = new ConcurrentHashMap<>(); + /** * 解析包含多个data节点的XML * @param xml 完整XML内容 - * @param wrapperClass 外层包装类(如VTTSBasic.class) * @param dataClass 内部data节点对应的实体类 */ - public List parseMultiDataXml( + public List parseMultiDataXml( String xml, - Class wrapperClass, Class dataClass) throws Exception { - JAXBContext context = JAXBContext.newInstance(wrapperClass, dataClass); - Unmarshaller unmarshaller = context.createUnmarshaller(); + // 1. 参数校验 + if (xml == null || xml.trim().isEmpty()) { + throw new IllegalArgumentException("XML内容不能为空"); + } - // 使用StAX提高安全性 + // 2. 创建包含包装类和数据类的JAXB上下文 + JAXBContext context = JAXBContext.newInstance( + VTTSBasic.class, + dataClass // 包含实际要解析的数据类型 + ); + // 3. 配置安全的Unmarshaller(防止XXE攻击) + Unmarshaller unmarshaller = context.createUnmarshaller(); XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); - XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); + // 4. 解析XML + try (StringReader reader = new StringReader(xml)) { +// XMLStreamReader xsr = xif.createXMLStreamReader(reader); +// JAXBElement root = unmarshaller.unmarshal(xsr, VTTSBasic.class); +// +// // 5. 获取解析结果 +// VTTSBasic wrapper = root.getValue(); +// List result = wrapper.getDataset(); +// +// // 6. 类型安全检查 +// if (!result.isEmpty() && !dataClass.isInstance(result.get(0))) { +// throw new ClassCastException("XML中包含类型不匹配的数据"); +// } +// +// return result; + // 创建 JAXBContext 时包含所有相关类 + // JAXBContext jc = JAXBContext.newInstance(VTTSBasic.class, Data.class); + //Unmarshaller unmarshaller = jc.createUnmarshaller(); - JAXBElement root = unmarshaller.unmarshal(xsr, wrapperClass); - Field datasetField = wrapperClass.getDeclaredField("dataset"); - datasetField.setAccessible(true); + // 解析 XML + XMLStreamReader xsr = xif.createXMLStreamReader(reader); + JAXBElement root = unmarshaller.unmarshal(xsr, VTTSBasic.class); - @SuppressWarnings("unchecked") - List result = (List) datasetField.get(root.getValue()); + // 获取解析结果 + VTTSBasic wrapper = root.getValue(); + List result = wrapper.getDataset(); - return result != null ? result : Collections.emptyList(); + return result; + } + } + + + @SuppressWarnings("unchecked") + public static VTTSBasic parseXml(String xml, Class dataType) throws Exception { + JAXBContext context = getOrCreateContext(VTTSBasic.class, dataType); + + Unmarshaller unmarshaller = context.createUnmarshaller(); + JAXBElement element = unmarshaller.unmarshal( + new StreamSource(new StringReader(xml)), + VTTSBasic.class + ); + + return element.getValue(); + } + + public static String toXml(VTTSBasic vttsBasic, Class dataType) throws Exception { + JAXBContext context = getOrCreateContext(VTTSBasic.class, dataType); + + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + + StringWriter writer = new StringWriter(); + marshaller.marshal(vttsBasic, writer); + + return writer.toString(); + } + + private static JAXBContext getOrCreateContext(Class... classes) throws JAXBException { + String key = Arrays.stream(classes) + .map(Class::getName) + .sorted() + .collect(Collectors.joining("|")); + + return CONTEXT_CACHE.computeIfAbsent(key.getClass(), k -> { + try { + // 为泛型类型创建JAXB上下文 + return JAXBContext.newInstance(classes); + } catch (JAXBException e) { + throw new RuntimeException("Failed to create JAXBContext", e); + } + }); } } diff --git a/target/classes/com/alihealth/d2d/provtest/controller/ProvTestServerController.class b/target/classes/com/alihealth/d2d/provtest/controller/ProvTestServerController.class index 898b51c3744f5ac2cd93c2f8b3e13cf5302186b9..1ae921dfd2dc7e645ff9d5ba1faef9c732e16ce3 100644 GIT binary patch delta 2179 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 diff --git a/target/classes/com/alihealth/d2d/provtest/domain/DomesticProducer.class b/target/classes/com/alihealth/d2d/provtest/domain/DomesticProducer.class index 85407ce6a6f8582d4e0f60fc37dd320dee372c82..5fe019fdb9307d6b6ca24a3412935cdcf0c7e422 100644 GIT binary patch delta 23 dcmexW_OEOMBOjy5WF|fX7DFJ|Y{ciG3jk%?2HXGu delta 18 ZcmexY_N#0IBOjx|WF|g?&1QTax&TOt1{44Q diff --git a/target/classes/com/alihealth/d2d/provtest/entity/VTTSBasic$KnownTypes.class b/target/classes/com/alihealth/d2d/provtest/entity/VTTSBasic$KnownTypes.class deleted file mode 100644 index f32ab9e0bc6ce812060cb45e9f8b5c368ed25b92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmbVJ%Sr<=6g{bJ$JW}`x9$`~e1HKdA}B(w2x3vOr3kLlnP5{gNtwx1`&q687k+>r zCEh7gcY-G5-g{1xb5F>}=i57g8di&#!h(k-56cYs6J?Y;WtiID=`ds)HjoUJrZVz6 z3A-|Ci>@YSzG;1-J0emk{wA}or(%ZUm9bBzJsL;?rAuRE)X*Z1Q+U+$ZODaIJt?&7 z@!%lfgUAk@j2)N8DL3MscDr>ZV&!lC2pyAb%O;U8FI3vUJUO)A7em3Yc9+oYPx5I*P}8kudghrwhnjbl3AmW-^7~M?q^#@MQj6SB=!Dd3zqt%-D3synHYCr= zzm05=_%b>!A&Z=c6^6tAy^*1s`s7-eM}E`oOW!f<{Xq*G$k1n|gV7{A6!V1m7z4T8 zSFjf%GVmzoi3Zj&O|dktb0|;+5}Bo#K^YawW!kIKntK5AF^?JIii9&*q+Iv{r(K*$ diff --git a/target/classes/com/alihealth/d2d/provtest/entity/VTTSBasic.class b/target/classes/com/alihealth/d2d/provtest/entity/VTTSBasic.class index 3f0b50d1a50df1c407ddf88abaf90ec1d700143b..8bbf188c116e9eeb3d6e36ac1e573c99f1770d82 100644 GIT binary patch literal 3314 zcmbVOYjYD-7=BK&o1|@8LMWx-q7@L+8;gjDX{i?4LNP714dv#AZL&5Co87wEbl|V} z1LO-IbVkdJ&N%u7XZ$h#0ui6HyJnNHmmHfc_)(vkan%w^UZ~$t1dXI$Zowqk%k>G66 za%yYN1MP}o8?Npd6*}78QcVP!YNM&V`V#Yv6WGVVSy{^c-ajxq~|S9K~&y311Y z43`<`Zrv3)5Z-I~DR0epa^KKVsViZ8+t4lAZFBMZ+I^$!O$^@=5KeoKg+~Ia<5dlp zG04g}88%7Kll%Rnc3wIvCeYg^B5>xl>SaW>N$-lMmme(X8-59MtoYL*Ro3lU8IwS& zs@JPQ7qBJf>GIO@{K70_BAsxJdc*Pr((Qhxnp@uYoM55|^NR)0(EWQ78u?A;&i!e)a#C0Vha)*byK$*v>&w3 zj^=_}%{z1Rv&EVJ9gr)AF>Tcy8acOH&UPAbX}pEE(>Q?sGzO7lV7pAo&WV%8G(Jq@ zJ>=8;vXC0ZSQ?jbS>RCTXW&x9v?{Vd2ewhnN2>H<8sA`@=^7gfxjv`L>KIOA9(h)F zOJ$I7R*jswc7=oTRUI8EhoEpmABa_(e6ziFl*c>#Bxd>fjPZ|9f&Ix{57z-chw5a; zBz$}y_Qj6)0{z?{4Fm?8lE=_R4ngO8h_cgM1y^<2QU>iYMjZkV^E`4O(H2aHk25b3Duc&F;adr}>MGmdr> zr=f)=ii{#A&5w-!j94cc8Q;c0AQ7S=T@5nQ!X?2oiOU&2DyKFw9R}aWStfLd$E#6; z@x2<9nHGK`TFyv=(wRXV;aO;Ukfwu}Kj?6Q?GnT+GISxytJTqw@1ZJBM*o2F9n|=f z(chsgjE!tT^|vGvi(`Bhl<_~&lT)5y|6`=bbIKNyO^H-NrAVrf$f;5$@eJ9==*_9Q zL{mt4LR(6t#sXBY20m<~TE%&M#6S*W1s7O%F+MZ+n7lX(V+sXQ3a(Jk1Su5@411DP zA_7Y_%tN5#B6UpB_7J*~sejOy#3#=&ki@6_Pl@Lk49Ko+@XmMSO<@Iwpy4yRCJRNC zXvv78Wge{)bvq)Jama{Tbt2Cqb#kG@o{*D_R)(nlZ>}oWLS|x8x#*|jXlA0yM5HpK zkC7bTLf1XXPZbn-kW<2wluQqgj=mc9bAi^-$v7RH!7QCr8R;CU7=LD;A@_GaD54NpAqXafc)@F}eM~YALTw5(Wt4Un#X5?W<2S^i!d1{#{#SJ0 zeu|!KdJDZ04ldcgfKF{;Up7N}3;R3AM39Z<30F?T*(EtDd literal 3212 zcmb7GYjYD-7=BK&n_IUflwOEXEsB_?EdfN-^rD3pD5M43SPI_QCTp{>*{z#Rg}>qt zkS~0Y87VU|Y0Yk3eC$K5kY8+-9cLY*b zZRbb3bbrgJ3uueBZMYXKyU#mWL6rs zXV#20vu>XJ)`dBnWgF7&##qAtLOE)S*GiuPFsXOw0CAdhMI}@Z5rNud#whtV*y5;)jASZ zYti0*Nxk}umQ&ks?&dEUWP$D(6$YAWnI-~Fv+b1`-Xq)%i`?l5YcRVvDhYXd$?&R9 z<+|>Y2%g~*&{XTKz@hM6%dLEMxLX?dl~*z2hlXy^Oozv78+VMdH#K%kKscR*4zC1M z$EzAHLy~np9x^g$fP?;3r%Y>C6X@+w5jeH4YUU(~dsjWZe0NFT@(IBfXQv7z%DTNE zbKLW-Erl7A~x!_ZZ+sX zAonZX1+SWKE-o$<=QX^C<0O(l!h9OdxvqX+4i^n0cw56^WEjpqx>&!ZZ^Z z#42XUy9hZvk6Fs1Onerfai?%>9K+|_saPWA=eUct!BP$D5ZE|R8yDz%6v=qvU-ZQ> z{~SYcEbxCqJjdZ+o7@F&s;h2_oLnJMt}--96jh=nGis-KJ5QQ4rz-Q18MS!QBz#z` znJ#}qPDOf=Jo$&K%C(T5npS4~QXEZBRauBcdi(+6lRN0SE#;|#A}=yZc$1Lj;nmTX z!+tif8p0kWFBfrzL8@fXRqi54u>*??JjyXLOqeC?trQ9hymP+m|)A+SOaObGFU z&syh*6bwRbDl~PJP7&3*$jVV3;!xo#$4mApG|sNx{=OX?kZ^G6PY3(d4i5JBbH9T_ z-D4ssO)${1Vb-l|q@;TSDPaR$!*%+N;0u4)%D!7+B%H>poT>s}8tmm8Odmu{(l*B4 GO#TmX+=kWw diff --git a/target/classes/com/alihealth/d2d/provtest/service/XmlParserService.class b/target/classes/com/alihealth/d2d/provtest/service/XmlParserService.class index 8a9fd918fb8da072cabcddb712188e1d8866e696..5db6331b55f2e0952a5c3e6e05da04afaa2b4221 100644 GIT binary patch literal 7078 zcmbtY349dQ8UMfRCbQX0t_6b2Wk6IQkX1yXo0J+tC};voA`q?OWOqo0?Cz|yvmvx< zwHDi2ZA)uy?P(7y+Sa1B;S#Zjw6*p=t-Y(&-uHdb|C^cJoz1}yTEcH<=DqKI?>qkA z@!pgFIs6!a6=Go+aqJCa8Qv^^-lE{n5Z)R_5O*nfn}WB8u^e~HpLc|z;++cK6~ep2 zcn{tyzwZg*eG1+mMkDT$-yf*K2XSv0AHscMgs@Le+^^t)FwVe-!*~%sqTr)p%*KO~ z@MDtiegzMO@Nf;z#61cg31b2F%8!rBw@-vIh653bdb9?Q;h6k+JcLh%@hN;-etbs3 z6V>E$n+_bT;Yg^y; z&W_IYT>|ypJGBulmbZ;mtVheX2~_tQgBi`vTe?6;`E0xUnbIDS=W-fMbe;Unzq0kA8pYzT`4`SXCy;qm!)O1I+-pznR+BP z?A@^~eSN)aw49OP$|_6GQ73_hiDGh2N+HnX&KYLRRkA2ufUc6}b{y~M>{)@nPtr<` zXK6bhfiomUPPmSpLbrn31=M^dP16r)sg#suuEWa*k}G`EG(Y)5It+RpO%3oYS+2iJ8>Zaa8eFQGp!k!*jh79JQ|71EHjx; z5DAiz=Y|rt+WY#PiP~OUOAIsS9H=|-`Gk{u7{T9UzF~f022t=W1>a^uQt%ytaMx%; z&)SBW$tif6{_oN4Au~zXYiZrqEr!ai?R{~d=Qa)O)DsNEQvkgy*m&gs&x(hf*mFW| z^C|4kkUL3?PZ9(niPb*(pRm;3pc1hDZ|zS*h=M08ZL^@}X{??y>5L1>5!=o=flv4Q zV`}zzy!R9Xj z?M+rE6PvAjDMsMDMOTey;bm=Kn(@~nnZ0Dq(itR?iEEjJhV|V7GbT*QA}4Uc#Qo%i zEAW(C&Q59Rfuy!@q6Q1kV|ks(v+Z=162$+!@lbG5Um;accrnbbKp@nfNO{FL+-v5o zguc#@rFRD5TUI(-Wo;0MZqBp#r1dRE&R|XM$Ye}gbFz=XqHfa~jODVje(sRzXP0RW z$8yA+ZrY5>SfL85cnL06@m)Nl;(Pc$(|oD$Y+!j9)KVSRU|tr*Vs?`v^>jaX>z?Nx zJoN0rJ6^cu=(7h8KY#yW$?FGf9!ea%V%2gQ_*3yi+@#`1__2KZi9oYt=en>|(*0$J z71|+bW#w#jNm`9uZ$6tfEt~pxjZ*E5mgDs=v z0@EF3it*`|X%#Qv=d1%Net}=I0I2vCey!p+_^pcH;bMVl&MGHP`i3lXm+YBT{2qT$ z@k-nzaIUkb)0NqlmdWiftu(#E7)r~2fDab{(xQKqyZlMTpYa#^ytL5iSxQ-jPT8rA z4zx;Enr-XJRx@w2HcwE&O))C|iodD&JDySS4;BB!zgVxPq^^wx|q=%s!IOWDm$Xs1{}OR6=vp77_nxn+@!*p1dwwvupmFri5*0P4602h~`v%)3x zGI(29G>MtaF_Q@4N2|vt3`LwVPGrCH-8AQlDc!}xX^NOGM3{R=up^&I$POU3&U+!{ z%u-G$laJw@mZ@!(C!eFiLW!q2!r^31ddQ+VyTMKz+qKxK|Rd8KiXLj+TY;qRT;|Z6a>^mpAqqtdtjgv--p&^6E>r+^m{LRzmL1k`eZ!89fgDSdK&M}h zGDuIsbP}iuEb(*kFy%`+{xDa>ELF@FXR4xE_K3IQHDx`bkr^?E^;oylNV9FwcDQ!| z%)LDG7QPg*fN1G9&EY%|r*6n(bjz`bZ1C z=_fz+Nq5VWul1YMhjk~RFTat7z1lkciafVJr27w=N=QeLcb zam5}!8#wlK%W>4UjGt?Hh8Z`(AeK{0Fii=h%3>`Xw?{|#Y53xG!zLP!t?;*)zRuP%y=B3 zcsLp!LsPsaS`$~JRmU+usvO5mxn$;jm>#c$tgs(p=0s7Nfy*| zLA`%L-7!?+cI?HSy!hRUS7D#`l!iLW%iAaTy$_E-eg`29w~klEKnv=ylym1{F)pNy zFXG+pN?LstE}=KJVk54_CZw?$yU>dpaRqsA!M%L$mxj-xhIH%sw}H3NY0g}N zOHs`==c5}v2y^8U^4dhYKJ39|xSWooJ3Lp(x6R}){rfa2uAu)HV?ViXMSxO0fNkif z+$*^AmAHz!zKoR3oOAcOntQoBT*Dn)9^1*|TE5?ga9#BYyaKffGz9|+5(<(EbOk#U z3@R8>U?|wBfM?Yd|3h%BCU}DDLsKnrXx5YN1g4>WN-Ay-!g$DINol3^1u`)CR$W1* zL3QClK1(&OgGCZz77x=zm3*%9VY7wUoYk|G*xb162xj+75HcPj2QWvXZZ1(5j0RoQ z?dRHn|{y3DvOa)SzSf zCK|`3-%fgW?rP3Ws$hw-yj8Sm3 z3AZ>gC1b@+jdJeQ{3^(PfrGOLyBu6#it%9$S_lah=3U`hpZ{r%Bug;G!6lCTXCMv<)_altQc8s-s4cU0H4# zD0O)>ymri*Eo?c5bKC%@oUT}~02cfJV8g!vuH=vu7WjBZcOG}X#~tbO!p{SpAO8I2 zI{;7M6B&b8U^pw|9L{^)qKpt;Vkn1Ek?}H?yzwhxyvndFBZ>>&__Zi5;`K1zkP*No z8JD5UxPqz|)ffyJS5aqJVK4=Ril%M4B>`eKwI?*hJ^$C zSC;1a1nU$>OO)6DpY6p;Wzw6Em0i8Mx}Y~J`W4IYO}It|GpsUL8171=c3yY%HN!O=k{l_Ubz67a4sAD!6^;KP6#}Crt^~6|ACre8 z_Tdav0f}7$eLo0P!F4zaT5!Fn{b_8FD{+Mv|n11MXPH@S%c_@UegsTn`QNd&+n^xyz|n zQ~^W$H#Wq7S1xA;cNhOv7rq$FB%-#le#UOOx?LsdNBK`Fl^;wb_$5*1_hUcO{n^7e z<6otdb;F%Cs+R5;we5nBdyB_%|5NrD-Hub$s?lnhR3?&|y^vetg~TCAq~rK6$=o@r z@vgj?hUHxwuTWGHW3B0Kc$b83ZFU=G$Aw8ef&qH3yk7tUs5I0M@u~iS&M?h|Xao#n zFZGHa2eA+PX(ZqP4no2Zg`A#In%fB8M_^eTD=$mqPsq)J^#T5tEadqDJVLJ~g6JZmNqt50KVmRsJ4f zRsI5@E%bj$=o0Y+CVVD2^kJ0Lj1$`lWRb@S9K$sAXX#V2geU2va1yJ~d}YlZ`)y^U zgIsyE-yV%Rr~2bMP#=NN`8Uu4xo7MzHi6(RhM@!vr^i zqXZuzz&MT*37JTMG9>@p5u)=C!c&CM%e+;f)Lz0#N_g7W7WfNoL>V-ONrs~Te}