XMLMapper允许在反序列化过程中使用任何根元素。

4

我有如下代码:

public class Xml {

    public static void main(String[] args) throws JsonProcessingException {

        String xmlString = "<password><plainPassword>12345</plainPassword></password>";

        XmlMapper xmlMapper = new XmlMapper();
        PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
        System.out.println(plainPassword.getPlainPassword());
    }

    @JacksonXmlRootElement(localName = "password")
    public static class PlainPassword {

        public String getPlainPassword() {
            return this.plainPassword;
        }

        public void setPlainPassword(String plainPassword) {
            this.plainPassword = plainPassword;
        }

        private String plainPassword;
    }
}

它的功能很好,但在xmlString中,我可以使用任何根标签名称,并且代码仍然可用。例如,String xmlString = "<x><plainPassword>12345</plainPassword></x>",其中我使用x作为根元素也可以工作。但是,有可能告诉xmlMapper它只能正确反序列化具有“password”根元素的字符串吗?
3个回答

1
我会以不同的方式处理。获取一个XPath实现,选择所有匹配//plainPassword的节点,然后获取每个节点的内容列表。
如果需要,您还可以获取父节点的名称;在找到节点的上下文中使用..来获取父节点。
查看XPath examples并尝试自己操作。请注意,您的代码可能因语言和XPath实现而有所不同。

1

很遗憾,你所描述的行为是Jackson支持的行为,如这个Github开放问题所示。

使用JSON内容和ObjectMapper,您可以启用UNWRAP_ROOT_VALUE反序列化功能,也许这对于此目的有所帮助,但我不确定XmlMapper是否正确支持了这个功能。

一个可能的解决方案是实现自定义反序列化器。

鉴于您的PlainPassword类:

@JacksonXmlRootElement(localName = "password")
public class PlainPassword {

  public String getPlainPassword() {
    return this.plainPassword;
  }

  public void setPlainPassword(String plainPassword) {
    this.plainPassword = plainPassword;
  }


  private String plainPassword;
}

考虑以下的main方法:
public static void main(String[] args) throws JsonProcessingException {

  String xmlString = "<x><plainPassword>12345</plainPassword></x>";

  XmlMapper xmlMapper = new XmlMapper();
  xmlMapper.registerModule(new SimpleModule().setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
          Class<?> beanClass = beanDesc.getBeanClass();
          JacksonXmlRootElement annotation = beanClass.getAnnotation(JacksonXmlRootElement.class);
          String requiredLocalName = null;
          if (annotation != null) {
            requiredLocalName = annotation.localName();
          }

          if (requiredLocalName != null) {
            return new EnforceXmlElementNameDeserializer<>(deserializer, beanDesc.getBeanClass(), requiredLocalName);

          }
          return deserializer;
        }
      }));

  PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
  System.out.println(plainPassword.getPlainPassword());
}

自定义反序列化器如下:

public class EnforceXmlElementNameDeserializer<T> extends StdDeserializer<T> implements ResolvableDeserializer {

  private final JsonDeserializer<?> defaultDeserializer;
  private final String requiredLocalName;

  public EnforceXmlElementNameDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> beanClass, String requiredLocalName) {
    super(beanClass);
    this.defaultDeserializer = defaultDeserializer;
    this.requiredLocalName = requiredLocalName;
  }

  @Override
  public T deserialize(JsonParser p, DeserializationContext ctxt)
      throws IOException {
    String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
    if (!this.requiredLocalName.equals(rootName)) {
      throw new IllegalArgumentException(
        String.format("Root name '%s' does not match required element name '%s'", rootName, this.requiredLocalName)
      );
    }

    @SuppressWarnings("unchecked")
    T itemObj = (T) defaultDeserializer.deserialize(p, ctxt);
    return itemObj;
  }

  @Override public void resolve(DeserializationContext ctxt) throws JsonMappingException {
    ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
  }
}

你需要在修改 BeanDeserializer 时实现 ResolvableDeserializer,否则反序列化会抛出异常。
这段代码基于 这个优秀的SO答案
该测试应该引发 IllegalArgumentException 并显示相应的消息:
Root name 'x' does not match required element name 'password'

请根据需要修改异常类型。
如果您使用以下代码:
String xmlString = "<password><plainPassword>12345</plainPassword></password>";

在你的main方法中,它应该能够无问题运行。

1
你可以将根类的名称更改为任何内容,例如:@JacksonXmlRootElement(localName = "xyz"),它能够正常工作。
根据Java文档JacksonXmlRootElement用于定义序列化时根级对象使用的根元素的名称(而不是反序列化映射),通常使用类型(类)的名称。

是的,但问题是如何在反序列化时生成错误,如果根元素不符合预期。 - Haster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接