JSON转Java POJO遇GSON解析异常,求XSD xs:choice场景解决方案
看起来你遇到的问题主要来自两个核心点:XML转JSON时单个元素被转为对象而非数组,以及xs:choice结构在POJO中的映射处理。我来一步步帮你梳理解决方案:
1. 先解决核心报错:MOVIMIENTO的数组/对象不匹配
你的错误Expected BEGIN_ARRAY but was BEGIN_OBJECT说明GSON期望MOVIMIENTO是数组,但实际JSON里它是单个对象。这是因为org.json的XML.toJSONObject默认逻辑:当XML中某个元素只有一个实例时,会转成JSON对象;多个实例才会转成数组。而你的XSD里MOVIMIENTO的maxOccurs="31",但minOccurs默认是1,所以当只有一条MOVIMIENTO数据时,转JSON就会变成对象,和POJO里定义的MOVIMIENTO[]类型不匹配。
修复方法:强制XML转JSON时保留数组结构
修改XML转JSON的代码,使用XMLParserConfiguration来强制让指定元素(或所有重复元素)始终以数组形式输出:
import org.json.XML; import org.json.XMLParserConfiguration; // 创建配置,开启"始终保留数组"模式 XMLParserConfiguration config = new XMLParserConfiguration(); config.setKeepArrays(true); // 用配置好的规则转XML为JSON JSONObject xmlJSONObj = XML.toJSONObject(xmlString, config);
这样不管MOVIMIENTO有1个还是31个,转出来的JSON里它都会是数组,GSON解析MOVIMIENTO[]就不会报错了。
2. 处理xs:choice的POJO映射
对于RESIDENCIA的二选一字段(ID_PAIS/ID_PROVINCIA_ISLA),你手动添加两个字段的做法是没问题的。GSON的默认行为会自动忽略JSON中不存在的字段,也就是说:
- 当JSON里有
ID_PROVINCIA_ISLA时,POJO的ID_PAIS会是null - 当JSON里有
ID_PAIS时,POJO的ID_PROVINCIA_ISLA会是null
完全符合xs:choice的业务逻辑,后续你只需要在代码里判断哪个字段不为null即可。
如果想要更严谨的约束(确保只有一个ID字段被赋值),也可以自定义GSON反序列化器:
public class ResidenciaDeserializer implements JsonDeserializer<RESIDENCIA> { @Override public RESIDENCIA deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { RESIDENCIA residencia = new RESIDENCIA(); JsonObject obj = json.getAsJsonObject(); // 解析MOVIMIENTO数组 residencia.MOVIMIENTO = context.deserialize(obj.get("MOVIMIENTO"), MOVIMIENTO[].class); // 处理二选一的ID字段 if (obj.has("ID_PAIS")) { residencia.ID_PAIS = obj.get("ID_PAIS").getAsString(); } else if (obj.has("ID_PROVINCIA_ISLA")) { residencia.ID_PROVINCIA_ISLA = obj.get("ID_PROVINCIA_ISLA").getAsString(); } return residencia; } }
然后注册这个反序列化器到GSON:
Gson gson = new GsonBuilder() .registerTypeAdapter(RESIDENCIA.class, new ResidenciaDeserializer()) .create();
3. 可选优化:跳过JSON中转,直接用JAXB解析XML
既然你有XSD文件,其实可以绕开XML转JSON的步骤,直接用JAXB生成POJO并解析XML,这样原生支持xs:choice结构,更准确也更高效。
步骤:
- 用JAXB的xjc工具根据XSD生成POJO类
- 直接用JAXBContext解析XML:
JAXBContext jaxbContext = JAXBContext.newInstance(ENCUESTA.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); ENCUESTA encuesta = (ENCUESTA) unmarshaller.unmarshal(new StringReader(xmlString));
JAXB生成的RESIDENCIA类会自动处理xs:choice,比如生成一个Object类型的字段或者带注解的二选一属性,完全符合XSD的约束。
内容的提问来源于stack exchange,提问作者davisoski

