杰克逊:XML-Binding - 处理空字符串与空字符串

时间:2014-01-08 17:50:43

标签: java xml-parsing jackson string xml-nil

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

在JAXB中,可以通过使用以下命令注释java变量来轻松完成此操作: @XmlElement(的nillable =真)

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

杰克逊可以这样做吗?

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

2 个答案:

答案 0 :(得分:3)

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

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

  • 序列化POJO中的值为xsi:nil元素,如果它们在POJO中为NULL,则为XML-String
  • 将硬编码类型(String,Integer,Float ...)列表反序列化为POJO,如果它们被定义为给定XML-String中的xsi:nil元素

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

班级项目:

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

Class Main:

    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);
      }
    }

Class 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;
      }

    }

答案 1 :(得分: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) 添加这些字段,使其满足您的需求。