Модуль Jackson xml: десериализация неизменяемого типа с использованием свойства @JacksonXmlText
Я хочу сериализовать неизменяемый тип как json, так и как xml:
Сериализованный JSON похож:
{
"text" : "... the text..."
}
и сериализованный xml выглядит так:
<asText>_text_</asText>
(обратите внимание, что текст представляет собой текст элемента xml)
Объект java выглядит как:
@JsonRootName("asText")
@Accessors(prefix="_")
public static class AsText {
@JsonProperty("text") @JacksonXmlText
@Getter private final String _text;
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
}
остерегайтесь, что свойство _text final (поэтому объект неизменен), и он аннотируется с помощью @JacksonXmlText
, чтобы быть сериализованным как текст элемента xml
Будучи объектом неизменяемым, требуется конструктор из текста, и аргумент конструктора должен быть аннотирован с помощью @JsonProperty
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
При сериализации/десериализации в/из JSON все работает отлично
... проблема возникает при сериализации/десериализации в/из XML:
// create the object
AsText obj = new AsText("_text_");
// init the mapper
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
log.warn("Serialized Xml\n{}",xml);
// Read from xml
log.warn("Read from Xml:");
AsText objReadedFromXml = mapper.readValue(xml,
AsText.class);
log.warn("Obj readed from serialized xml: {}",
objReadedFromXml.getClass().getName());
Исключение составляет:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class r01f.types.url.UrlQueryStringParam), not marked as ignorable (2 known properties: "value", "name"])
Кажется, что для модуля xml требуется, чтобы конструктор объекта был аннотирован следующим образом:
public AsText(@JsonProperty("") final String text) {
_text = text;
}
НО это НЕ РАБОТАЕТ:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `test.types.SerializeAsXmlElementTextTest$AsText` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Аннотации @JsonProperty("text")
в аргументе конструктора необходимы для десериализации из JSON
... как я могу заставить это работать
Ответы
Ответ 1
Попробуйте добавить публичный getter для свойства. Я считаю, что это должно устранить проблему десериализации.
@JsonRootName("asText")
@Accessors(prefix = "_")
public static class AsText {
@JsonProperty("text")
@JacksonXmlText
@Getter
private final String _text;
public AsText(@JsonProperty("text") final String text) {
_text = text;
}
public String getText() {
return _text;
}
}
На самом деле, он работает без добавления геттера тоже с этими версиями Ломбака и Джексона.
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
</dependencies>
Ответ 2
У меня была такая же проблема с ошибкой "нет создателя на основе делегата или основанного на свойствах". В моем случае это проблема с версией версии 2.6.6. Я исправил его с понижением до версии 2.5.5. Версия 2.5.6 доступна в mvnrepository.com, но на официальной странице установлена стабильная версия с надписью 2.5.5.
Ответ 3
Похоже, что основной причиной вашей проблемы является то, что вы пытаетесь использовать сериализацию JSON и XML одновременно, но с разными конфигурациями. К сожалению, XmlMapper
наследует от ObjectMapper
и наследует всю специфичную для JSON конфигурацию (и вы можете переопределить ее, но не можете очистить ее с помощью аннотаций, специфичных для XML), что и является причиной вашего конфликта. Похоже, что самый простой способ работать над этим - использовать @JsonAlias
аннотацию в конструкторе. Это немного хакерский, но он работает. Вот минимальный пример (без Lombok), который работает для меня:
@JsonRootName("asText")
public static class AsText {
@JsonProperty("text")
@JacksonXmlText
private final String _text;
public AsText(@JsonAlias("") @JsonProperty("text") final String text) {
_text = text;
}
@JsonIgnore
public String getText() {
return _text;
}
@Override
public String toString() {
return "AsText{" +
"_text='" + _text + '\'' +
'}';
}
}
Обратите внимание, что я также добавил @JsonIgnore
в getter, потому что иначе я не получил запрошенный XML-формат (и вы можете сделать то же самое с помощью Lombok onMethod
, как описано в onX).
Для простого теста:
public static void main(String[] args) throws IOException {
// create the object
AsText obj = new AsText("123_text_");
// init the mapper
//ObjectMapper mapper = new ObjectMapper();
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
System.out.println("Serialized Xml\n" + xml);
// Read from xml
AsText objReadedFromXml = mapper.readValue(xml, AsText.class);
System.out.println("Read from Xml: " + objReadedFromXml);
}
Я получаю следующий вывод:
Сериализованный Xml
<asText> 123_text_ </asText>
Чтение из Xml: AsText {_text = '123_text _'}