首页 > 解决方案 > 使用杰克逊没有字符串参数构造函数/工厂方法的 XML 映射来反序列化字符串值(“比利时华夫饼”)

问题描述

出于学习目的,我正在尝试将 xml 文件映射到 pojo。我的 xml 看起来像这样:

<breakfast_menu>
   <food>
     <name>Belgian Waffles</name>
     <price>$5.95</price>
     <description>
        Two of our famous Belgian Waffles with plenty of real maple syrup
     </description>
     <calories>650</calories>
   </food>
   <food>
     <name>Strawberry Belgian Waffles</name>
     <price>$7.95</price>
     <description>
        Light Belgian waffles covered with strawberries and whipped cream
     </description>
     <calories>900</calories>
   </food>
</breakfast_menu>

Breakfes实体类

@JacksonXmlRootElement(localName = "breakfast_menu")
public class BreakfestFood {

    private List<Food> food;

    public BreakfestFood() {}

    public List<Food> getFood() {
        return food;
    }

    public void setFood(List<Food> food) {
        this.food = food;
    }

}

食品实体类

@JacksonXmlRootElement(localName = "food")
public class Food {

    @JacksonXmlProperty(localName = "name")
    private String name;
    @JacksonXmlProperty
    private double price;
    @JacksonXmlProperty
    private String description;
    @JacksonXmlProperty
    private int calories;

主要功能:

public static void main(String[] args) {
    File file = new File("X:\\food.xml");
    XmlMapper xmlMapper = new XmlMapper();

    try {
        String xml = inputStreamToString(new FileInputStream(file));
        BreakfestFood value = xmlMapper.readValue(xml, BreakfestFood.class);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

当我运行这段代码时,我得到这样的堆栈:String-argument constructor/factory method to deserialize from String value ('Belgian Waffles')。所以它似乎无法映射食物的名称值。有人知道出了什么问题吗?

标签: javaxmljackson

解决方案


您的映射不正确。为了检查有什么问题,我鼓励您序列化一个简单的对象。

BreakfestFood bf = new BreakfestFood();
Food f1 = new Food();
f1.setName("f1Name");
f1.setCalories(20);
f1.setDescription("desc");
f1.setPrice(11.1);
bf.setFood(List.of(f1));

System.out.println(xmlMapper.writeValueAsString(bf));

Output: 
<breakfast_menu>
  <food>
    <food>
      <price>11.1</price>
      <description>desc</description>
      <calories>20</calories>
      <name>f1Name</name>
    </food>
  </food>
</breakfast_menu>

如您所见,您会得到一个额外的列表包装器。要摆脱它,请使用:

@JacksonXmlRootElement(localName = "breakfast_menu")
public class BreakfestFood {

    // Use this to change element name:
    // @JacksonXmlProperty(localName = "food")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<Food> food;

    // getters and setters
}

最重要的是:

  • 如果你有公共吸气剂,Food 中的注释是多余的
  • 价格标签中的值不是双倍的。您需要调整映射以解析货币值。定制是解决这个问题的一个想法@JsonSerialize@JsonDeserialize
  • 更一般地说,将价格存储为双倍是一个糟糕的主意。

推荐阅读