这个问题是关于JacksonXMLMap序列化和非序列化的。默认情况下,Jackson希望Map的键是XML元素本身。
<MyMap>
<key1>value1</key1>
<key2>value2</key2>
</MyMap>
但这使得不可能有无效XML元素名称的键。
在Jackson中,是否有一种方法可以通过 Package 条目XML元素(如下所示)将XML封装起来?
<MyMap>
<MyEntry>
<Key>key1</Key>
<Value>value1</Value>
</MyEntry>
<MyEntry>
<Key>key2</Key>
<Value>value2</Value>
</MyEntry>
</MyMap>
我见过@JacksonXmlElementWrapper
,但它似乎不允许命名实体的XML元素,而是命名Map的XML元素。我不知道这是一个bug还是预期的行为。但我希望有办法解决。
我使用的演示代码以防有人想复制
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.*;
import lombok.*;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
public class ReproductionTest {
private static final String XML = """
<MyRoot>
<MyMap>
<MyEntry><Key>key1</Key><Value>value1</Value></MyEntry>
<MyEntry><Key>key2</Key><Value>value2</Value></MyEntry>
<!--33Key3>Not an option since I don't control keys to have valid XML names</33Key3-->
</MyMap>
<MyString>abc</MyString>
</MyRoot>""";
@NoArgsConstructor
@Getter
@Setter
@ToString
static class MyRoot {
@JacksonXmlProperty(localName = "MyMap")
// With the next line uncommented, the following exception is thrown:
// UnrecognizedPropertyException: Unrecognized field "MyMap"...
// not marked as ignorable (2 known properties: "MyString", "MyEntry"])
//@JacksonXmlElementWrapper(localName = "MyEntry")
Map<String, String> myMap;
@JacksonXmlProperty(localName = "MyString")
String myString;
}
@Test
public void reproducesDeserializationIssue() throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
MyRoot myRoot = xmlMapper.readValue(XML, MyRoot.class);
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); // re-enforcing default
assertEquals("abc", myRoot.getMyString()); // passes
assertEquals(1, myRoot.getMyMap().size()); // passes
assertNotNull(myRoot.getMyMap().get("MyEntry")); // passes, unexpectedly
assertEquals("", myRoot.getMyMap().get("MyEntry")); // passes, unexpectedly
assertNull(myRoot.getMyMap().get("key1")); // passes, unexpectedly
assertNull(myRoot.getMyMap().get("key2")); // passes, unexpectedly
System.out.println(xmlMapper.writeValueAsString(myRoot));
// Prints <MyRoot><myMap><myItem></myItem></myMap><myString>abc</myString></MyRoot>
}
}
P.S.在这一点上,我不能控制XML模式,也不能输出POJO。但我可以控制XmlMapper配置。因此,我的理解是,我的选项仅限于MixIns和自定义反序列化器。但希望有更简单的解决办法。
1条答案
按热度按时间ffdz8vbo1#
正如你在你的帖子中所观察到的,问题在于这样一个事实,即通常的标准Map方式不能应用于你提供的xml,但是你可以将xml解释为
MyEntry
类的列表,并将其Map为列表。从初始xml开始:你可以定义两个类
MyRoot
和MyEntry
,如下所示:你可以注意到,现在
myMap
是一个List
示例,它包含了MyEntry
对象,现在你可以用预期的结果来格式化xml: