无法使用 Jackson XmlMapper 反序列化包装列表
Can't deserialize a wrapped List with Jackson XmlMapper
我正在尝试创建一对不可变的 POJO 来处理 XML 的序列化和反序列化,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Outer xmlns="http://example.com">
<Foo>outer foo</Foo>
<Inners>
<Inner>
<Bar>inner 1 bar</Bar>
<Baz>inner 2 baz</Baz>
</Inner>
<Inner>
<Bar>inner 2 bar</Bar>
<Baz>inner 2 baz</Baz>
</Inner>
</Inners>
</Outer>
我需要能够序列化和反序列化包含 Inner
列表的 Outer
和一个 Inner
本身。
我可以毫无问题地为此创建一个序列化程序,但我的反序列化程序失败,出现异常 com.fasterxml.jackson.databind.JsonMappingException: Duplicate property 'Inners' for [simple type, class BrokenTest$Outer]
以下是通过序列化和反序列化失败的单元测试:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
public class BrokenTest
{
private static final String NAMESPACE = "http://example.com";
@JacksonXmlRootElement(localName="Outer", namespace = NAMESPACE)
public static class Outer
{
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE)
public final String foo;
@JacksonXmlProperty(localName="Inner", namespace = NAMESPACE)
@JacksonXmlElementWrapper(localName = "Inners", namespace = NAMESPACE)
public final List<Inner> inners;
@JsonCreator
public Outer(
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE) final String foo,
@JacksonXmlProperty(localName="Inners", namespace = NAMESPACE) final List<Inner> inners)
{
this.foo = foo;
this.inners = inners;
}
}
@JacksonXmlRootElement(localName="Inner", namespace = NAMESPACE)
public static class Inner
{
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE)
public final String bar;
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE)
public final String baz;
@JsonCreator
public Inner(
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE) final String bar,
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE) final String baz)
{
this.bar = bar;
this.baz = baz;
}
}
@Test
public void serializeInner() throws Exception
{
Inner inner = new Inner("inner bar", "inner baz");
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(inner);
assertEquals(serialized, "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>");
}
@Test
public void deserializeInner() throws Exception
{
String serialized = "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>";
ObjectMapper mapper = new XmlMapper();
Inner inner = mapper.readValue(serialized, Inner.class);
assertNotNull(inner);
assertEquals("inner bar", inner.bar);
assertEquals("inner baz", inner.baz);
}
@Test
public void serializeOuter() throws Exception
{
Outer outer = new Outer("outer foo", Arrays.asList(new Inner("inner 1 bar", "inner 1 baz"), new Inner("inner 2 bar", "inner 2 baz")));
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(outer);
assertEquals(serialized, "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>");
}
@Test
public void deserializeOuter() throws Exception
{
String serialized = "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>";
ObjectMapper mapper = new XmlMapper();
Outer outer = mapper.readValue(serialized, Outer.class); // fails
assertNotNull(outer);
assertEquals("outer foo", outer.foo);
assertEquals(2, outer.inners.size());
assertEquals("inner 1 bar", outer.inners.get(0).bar);
assertEquals("inner 1 baz", outer.inners.get(0).baz);
assertEquals("inner 2 bar", outer.inners.get(1).bar);
assertEquals("inner 2 baz", outer.inners.get(1).baz);
}
}
如果我将 Outer
的构造函数上的 @JacksonXmlProperty
注释更改为使用 localName
"Inner",我可以获得不同的异常 (com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Inners" (class BrokenTest$Outer), not marked as ignorable (2 known properties: "Foo", "Inner"])
)而不是 "Inners".
有没有办法创建一对适用于这四个测试用例的 POJO?
编辑:这是使用 Jackson 版本 2.7.3
这是杰克逊 XML 处理中的 known bug。
解决方法是在 Outer
构造函数中更改 inners
上的注释,这样本地名称就不会出现在 XML 文档中。
这是一个有效的单元测试版本:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
public class BrokenTest
{
private static final String NAMESPACE = "http://example.com";
@JacksonXmlRootElement(localName="Outer", namespace = NAMESPACE)
public static class Outer
{
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE)
public final String foo;
@JacksonXmlProperty(localName="Inner", namespace = NAMESPACE)
@JacksonXmlElementWrapper(localName = "Inners", namespace = NAMESPACE)
public final List<Inner> inners;
@JsonCreator
public Outer(
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE) final String foo,
@JacksonXmlProperty(localName="XXX", namespace = NAMESPACE) final List<Inner> inners)
{
this.foo = foo;
this.inners = inners;
}
}
@JacksonXmlRootElement(localName="Inner", namespace = NAMESPACE)
public static class Inner
{
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE)
public final String bar;
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE)
public final String baz;
@JsonCreator
public Inner(
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE) final String bar,
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE) final String baz)
{
this.bar = bar;
this.baz = baz;
}
}
@Test
public void serializeInner() throws Exception
{
Inner inner = new Inner("inner bar", "inner baz");
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(inner);
assertEquals(serialized, "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>");
}
@Test
public void deserializeInner() throws Exception
{
String serialized = "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>";
ObjectMapper mapper = new XmlMapper();
Inner inner = mapper.readValue(serialized, Inner.class);
assertNotNull(inner);
assertEquals("inner bar", inner.bar);
assertEquals("inner baz", inner.baz);
}
@Test
public void serializeOuter() throws Exception
{
Outer outer = new Outer("outer foo", Arrays.asList(new Inner("inner 1 bar", "inner 1 baz"), new Inner("inner 2 bar", "inner 2 baz")));
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(outer);
assertEquals(serialized, "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>");
}
@Test
public void deserializeOuter() throws Exception
{
String serialized = "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>";
ObjectMapper mapper = new XmlMapper();
Outer outer = mapper.readValue(serialized, Outer.class); // fails
assertNotNull(outer);
assertEquals("outer foo", outer.foo);
assertEquals(2, outer.inners.size());
assertEquals("inner 1 bar", outer.inners.get(0).bar);
assertEquals("inner 1 baz", outer.inners.get(0).baz);
assertEquals("inner 2 bar", outer.inners.get(1).bar);
assertEquals("inner 2 baz", outer.inners.get(1).baz);
}
}
我正在尝试创建一对不可变的 POJO 来处理 XML 的序列化和反序列化,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Outer xmlns="http://example.com">
<Foo>outer foo</Foo>
<Inners>
<Inner>
<Bar>inner 1 bar</Bar>
<Baz>inner 2 baz</Baz>
</Inner>
<Inner>
<Bar>inner 2 bar</Bar>
<Baz>inner 2 baz</Baz>
</Inner>
</Inners>
</Outer>
我需要能够序列化和反序列化包含 Inner
列表的 Outer
和一个 Inner
本身。
我可以毫无问题地为此创建一个序列化程序,但我的反序列化程序失败,出现异常 com.fasterxml.jackson.databind.JsonMappingException: Duplicate property 'Inners' for [simple type, class BrokenTest$Outer]
以下是通过序列化和反序列化失败的单元测试:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
public class BrokenTest
{
private static final String NAMESPACE = "http://example.com";
@JacksonXmlRootElement(localName="Outer", namespace = NAMESPACE)
public static class Outer
{
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE)
public final String foo;
@JacksonXmlProperty(localName="Inner", namespace = NAMESPACE)
@JacksonXmlElementWrapper(localName = "Inners", namespace = NAMESPACE)
public final List<Inner> inners;
@JsonCreator
public Outer(
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE) final String foo,
@JacksonXmlProperty(localName="Inners", namespace = NAMESPACE) final List<Inner> inners)
{
this.foo = foo;
this.inners = inners;
}
}
@JacksonXmlRootElement(localName="Inner", namespace = NAMESPACE)
public static class Inner
{
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE)
public final String bar;
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE)
public final String baz;
@JsonCreator
public Inner(
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE) final String bar,
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE) final String baz)
{
this.bar = bar;
this.baz = baz;
}
}
@Test
public void serializeInner() throws Exception
{
Inner inner = new Inner("inner bar", "inner baz");
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(inner);
assertEquals(serialized, "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>");
}
@Test
public void deserializeInner() throws Exception
{
String serialized = "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>";
ObjectMapper mapper = new XmlMapper();
Inner inner = mapper.readValue(serialized, Inner.class);
assertNotNull(inner);
assertEquals("inner bar", inner.bar);
assertEquals("inner baz", inner.baz);
}
@Test
public void serializeOuter() throws Exception
{
Outer outer = new Outer("outer foo", Arrays.asList(new Inner("inner 1 bar", "inner 1 baz"), new Inner("inner 2 bar", "inner 2 baz")));
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(outer);
assertEquals(serialized, "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>");
}
@Test
public void deserializeOuter() throws Exception
{
String serialized = "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>";
ObjectMapper mapper = new XmlMapper();
Outer outer = mapper.readValue(serialized, Outer.class); // fails
assertNotNull(outer);
assertEquals("outer foo", outer.foo);
assertEquals(2, outer.inners.size());
assertEquals("inner 1 bar", outer.inners.get(0).bar);
assertEquals("inner 1 baz", outer.inners.get(0).baz);
assertEquals("inner 2 bar", outer.inners.get(1).bar);
assertEquals("inner 2 baz", outer.inners.get(1).baz);
}
}
如果我将 Outer
的构造函数上的 @JacksonXmlProperty
注释更改为使用 localName
"Inner",我可以获得不同的异常 (com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Inners" (class BrokenTest$Outer), not marked as ignorable (2 known properties: "Foo", "Inner"])
)而不是 "Inners".
有没有办法创建一对适用于这四个测试用例的 POJO?
编辑:这是使用 Jackson 版本 2.7.3
这是杰克逊 XML 处理中的 known bug。
解决方法是在 Outer
构造函数中更改 inners
上的注释,这样本地名称就不会出现在 XML 文档中。
这是一个有效的单元测试版本:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
public class BrokenTest
{
private static final String NAMESPACE = "http://example.com";
@JacksonXmlRootElement(localName="Outer", namespace = NAMESPACE)
public static class Outer
{
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE)
public final String foo;
@JacksonXmlProperty(localName="Inner", namespace = NAMESPACE)
@JacksonXmlElementWrapper(localName = "Inners", namespace = NAMESPACE)
public final List<Inner> inners;
@JsonCreator
public Outer(
@JacksonXmlProperty(localName="Foo", namespace = NAMESPACE) final String foo,
@JacksonXmlProperty(localName="XXX", namespace = NAMESPACE) final List<Inner> inners)
{
this.foo = foo;
this.inners = inners;
}
}
@JacksonXmlRootElement(localName="Inner", namespace = NAMESPACE)
public static class Inner
{
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE)
public final String bar;
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE)
public final String baz;
@JsonCreator
public Inner(
@JacksonXmlProperty(localName="Bar", namespace = NAMESPACE) final String bar,
@JacksonXmlProperty(localName="Baz", namespace = NAMESPACE) final String baz)
{
this.bar = bar;
this.baz = baz;
}
}
@Test
public void serializeInner() throws Exception
{
Inner inner = new Inner("inner bar", "inner baz");
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(inner);
assertEquals(serialized, "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>");
}
@Test
public void deserializeInner() throws Exception
{
String serialized = "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>";
ObjectMapper mapper = new XmlMapper();
Inner inner = mapper.readValue(serialized, Inner.class);
assertNotNull(inner);
assertEquals("inner bar", inner.bar);
assertEquals("inner baz", inner.baz);
}
@Test
public void serializeOuter() throws Exception
{
Outer outer = new Outer("outer foo", Arrays.asList(new Inner("inner 1 bar", "inner 1 baz"), new Inner("inner 2 bar", "inner 2 baz")));
ObjectMapper mapper = new XmlMapper();
String serialized = mapper.writeValueAsString(outer);
assertEquals(serialized, "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>");
}
@Test
public void deserializeOuter() throws Exception
{
String serialized = "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>";
ObjectMapper mapper = new XmlMapper();
Outer outer = mapper.readValue(serialized, Outer.class); // fails
assertNotNull(outer);
assertEquals("outer foo", outer.foo);
assertEquals(2, outer.inners.size());
assertEquals("inner 1 bar", outer.inners.get(0).bar);
assertEquals("inner 1 baz", outer.inners.get(0).baz);
assertEquals("inner 2 bar", outer.inners.get(1).bar);
assertEquals("inner 2 baz", outer.inners.get(1).baz);
}
}