3

新的 Jackson-API 为我们提供了方便的 XML-Binding(就像 JAXB 一样),但我找不到任何方法让 Jackson 序列化典型的“xsi:nil”-属性,这是在 XML 中表示 NULL 值的事实上的标准? 如果我看到这个错误,请纠正我;-)

在 JAXB 中,这可以通过注释 java 变量来轻松完成:@XMLElement(nillable=true)

另见:http ://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html

杰克逊能做到吗?

对于 Jackson-XML,请参见:https ://github.com/FasterXML/jackson-dataformat-xml

4

2 回答 2

3

这不能回答问题,但提供了一种解决方法(非常hacky)!

我设法为杰克逊编写了一些自定义序列化器/反序列化器(直到杰克逊正式支持 xsi:nil),它允许以下内容:

  • 如果 POJO 中的值为 NULL,则将 POJO 中的值作为 xsi:nil 元素序列化为 XML-String
  • 如果硬编码类型(字符串、整数、浮点数...)列表在给定 XML 字符串中定义为 xsi:nil 元素,则将它们反序列化为 NULL 到 POJO

使用此代码,可以为其他 xml 绑定库 (JAXB..) 提供互操作性,这些库只能与 xsi:nil 一起用于空值。

班级项目:

    public class Item {          
      public String x;
      public Integer y;        
      public Integer z;
    }

主类:

    public class Main {

      public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        NumberDeserializers numberDeserializers = new NumberDeserializers();

        XmlMapper xmlMapper = new XmlMapper();    
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // create custom-serialization
        XmlSerializerProvider provider = new XmlSerializerProvider(new XmlRootNameLookup());
        provider.setNullValueSerializer(new MyNullSerializer());
        xmlMapper.setSerializerProvider(provider);

        // create custom deserialization
        SimpleModule myModule = new SimpleModule("Module", new Version(1, 9, 10, "FINAL"));    
        myModule.addDeserializer(String.class, new NullableDeserializer(new StringDeserializer()));            
        myModule.addDeserializer(Number.class, new NullableDeserializer(numberDeserializers.find(Integer.class, Integer.class.getName())));
        myModule.addDeserializer(Float.class, new NullableDeserializer(numberDeserializers.find(Float.class, Float.class.getName())));

        xmlMapper.registerModule(myModule);  

        // deserialize
        Item value = xmlMapper.readValue(
            "<item xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ><a></a><x xsi:nil=\"true\"></x><y/><z>13</z></item>", 
            Item.class);

        // serialize
        String xml = xmlMapper.writeValueAsString(value); 
        System.out.println(xml);
      }
    }

MyNullSerializer 类:

    public class MyNullSerializer extends JsonSerializer<Object> {
      @Override
      public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        ToXmlGenerator xGen = (ToXmlGenerator) jgen;

        xGen.writeStartObject();
        try {
          xGen.getStaxWriter().writeAttribute("xsi:nil", "true");
        } catch (Exception e){
          e.printStackTrace();
        }
        xGen.writeEndObject();    
      }
    }

MyNullDeserializer 类:

    public class MyNullDeserializer extends JsonDeserializer {

      private JsonDeserializer delegate; 

      public MyNullDeserializer(JsonDeserializer delegate){
        this.delegate = delegate;
      }

      @Override
      public Object deserialize(JsonParser jp, DeserializationContext ctxt)
          throws IOException, JsonProcessingException {

        FromXmlParser fxp = (FromXmlParser) jp;

        boolean isNil = false;

        XMLStreamReader reader = fxp.getStaxReader();
        if (reader.isStartElement()){
          if (reader.getAttributeCount() > 0){
            String atVal = reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
            if (atVal != null){
              if (Boolean.parseBoolean(atVal) == true){
                isNil = true;
              }
            }
          }
        }

        Object value = null;
        if (isNil == false){
          value = delegate.deserialize(jp, ctxt);
        } else {
          jp.getValueAsString(); // move forward
        }
        return value;
      }

    }
于 2014-02-09T17:30:48.930 回答
2

我扩展了 rnd 的工作,因为它为所有字段启用了该功能,而不仅仅是其中一些。

这是一个您将添加到绑定中的模块,如下所示:

XmlMapper mapper = new XmlMapper();
    
XmlSerializerProvider provider = new XmlSerializerProvider(new XmlRootNameLookup());
provider.setNullValueSerializer(new NullSerializer());
mapper.setSerializerProvider(provider);
mapper.registerModule(new NullPointerModule());

NullPointerModule 实现了自己的自定义序列化程序来传递自省当前字段所需的属性。

NullPointerModule.java:

public class NullPointerModule extends SimpleModule implements java.io.Serializable {
    private static final long serialVersionUID = 1L;

    @Override
    public void setupModule(SetupContext context) {
        // Need to modify BeanDeserializer, BeanSerializer that are used
        context.addBeanSerializerModifier(new XmlBeanSerializerModifier() {
            @Override
            public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
                for (int i = 0, len = beanProperties.size(); i < len; ++i) {
                    BeanPropertyWriter bpw = beanProperties.get(i);
                    if (bpw.getClass().equals(BeanPropertyWriter.class)) {
                        beanProperties.set(i, new NullCheckedBeanPropertyWriter(bpw));
                    }
                }
                return beanProperties;
            }
        });
        super.setupModule(context);
    }

}

接下来是实际的 NullSerializer,它接受属性编写器并确定该字段是否需要 nil 字段。

NullSerializer.java:

public class NullSerializer extends JsonSerializer<Object> {

    @SuppressWarnings("unused")
    public void serializeWithProperty(BeanPropertyWriter propertyWriter, Object value, JsonGenerator jgen, SerializerProvider provider) {
        ToXmlGenerator xGen = (ToXmlGenerator) jgen;
        XmlElement annotation = null;

        if (propertyWriter != null) {
            AnnotatedMember member = propertyWriter.getMember();
            annotation = member.getAnnotation(XmlElement.class);
        }

        try {
            if (annotation != null) {
                if (annotation.nillable()) {
                    xGen.writeStartObject();
                    XMLStreamWriter staxWriter = xGen.getStaxWriter();

                    staxWriter.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
                    staxWriter.writeAttribute("xsi:nil", "true");
                    xGen.writeEndObject();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        serializeWithProperty(null, value, jgen, provider);
    }
}

最后是 propertyWriters 的覆盖。这有点小技巧,因为如果属性编写器本身被另一个模块中的另一个类替换,这可能会失败。

NullCheckedBeanPropertyWriter.java:

public class NullCheckedBeanPropertyWriter extends BeanPropertyWriter {
    public NullCheckedBeanPropertyWriter(BeanPropertyWriter base) {
        super(base);
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        final Object value = (_accessorMethod == null) ? _field.get(bean)
                : _accessorMethod.invoke(bean);

        // Null handling is bit different, check that first
        if (value == null) {
            if (_nullSerializer != null) {
                gen.writeFieldName(_name);
                if (_nullSerializer instanceof NullSerializer) {
                    NullSerializer nullSerializer = (NullSerializer) _nullSerializer;
                    nullSerializer.serializeWithProperty(this, bean, gen, prov);
                    return;
                }
                _nullSerializer.serialize(null, gen, prov);
            }
            return;
        }
        super.serializeAsField(bean, gen, prov);
    }
}

然后可以使用 @XmlElement(nillable=true) 添加这些字段,以使它们满足您的需求。

于 2021-03-18T06:15:39.640 回答