Десериализация нескольких разных тегов (с одним и тем же именем) с помощью SimpleFramework
Я использую Simple Framework для сериализации / десериализации XML, и хотя первое легко, у меня есть проблемы с последним. Итак, я получаю ответ XML от сервера, и это выглядит так:
<?xml version="1.0" ?>
<tables>
<table name="result" a="context" b="name">
<r a="stuff1" b="blahblah" />
</table>
<table name="response" a="error" b="reason">
<r a="0" b="" />
</table>
</tables>
Да, он имеет 2 элемента с именем "таблица". Уловка в том, что первый элемент "table" может иметь более 3 атрибутов, что означает, что я не могу просто создать универсальный объект для тега "table". Итак, мой текущий код для десериализованной сущности выглядит так:
@Root(name = "tables", strict = false)
public class Response {
@Element(name = "table", required = false)
@Path("//table[@name='result']")
Result resultTable;
@Element(name = "table")
@Path("//table[@name='result']")
Response responseTable;
public Result getResultTable() {
return resultTable;
}
public void setResultTable(Result resultTable) {
this.resultTable = resultTable;
}
public Response getResponseTable() {
return responseTable;
}
public void setResponseTable(Response responseTable) {
this.responseTable = responseTable;
}
}
К сожалению, это не работает: я получаю исключение:
org.simpleframework.xml.core.PathException: Path '//[@name='result']' in field 'resultTable'
com.package.Response.resultTable references document root
Я пробовал разные варианты XPath, такие как просто подстановочные узлы:
@Path("//*[@name='result']")
@Path("*[@name='result']")
но это тоже не сработало. Итак, это моя вина из-за неправильных опций XPath или ограничений Simple Framework:
При использовании таких аннотаций следует отметить, что поддерживается только подмножество синтаксиса выражений XPath. Например, ссылки на элементы и атрибуты не могут быть взяты из корня документа, разрешены только ссылки в текущем контексте.
и я должен тогда сделать это с другими десериализаторами XML? Благодарю.
1 ответ
Вы можете попробовать обойти это, используя встроенный список. Другое - и, на мой взгляд, лучшее решение: использовать Converter
для части "если имя является результатом десериализации как результат, если ответ десериализовать как ответ" часть. Это звучит сложнее, чем есть на самом деле!
Классы
Так как есть два Response
классы - один используется для tables
и один как table
я назвал последний ResponseTable
; ResultTable
является Result
в вашем примере. Я думаю, у вас есть несколько пакетов, чтобы предотвратить это.
Для обеих таблиц класс Content
используется для моделирования <r ... />
элемент.
ResponseTable
@Root
public class ResponseTable // 'Response' in your code
{
@Attribute(name = "name")
private String name;
@Attribute(empty = "a")
private String a;
@Attribute(empty = "b")
private String b;
@Element(name = "r")
private Content r;
// ...
}
ResultTable
@Root
public class ResultTable // 'Result' in your code
{
@Attribute(name = "name")
private String name;
@Attribute(empty = "a")
private String a;
@Attribute(empty = "b")
private String b;
@Element(name = "r")
private Content r;
// ...
}
содержание
@Root()
public class Content
{
@Attribute(name = "a")
private String a;
@Attribute(name = "b")
private String b;
// ...
}
отклик
И вот начинается интересная часть:
@Root(name = "tables", strict = false)
@Convert(Response.ResponseConverter.class)
public class Response
{
@Element(name = "table")
private ResultTable resultTable;
@Element(name = "table2")
private ResponseTable responeTable;
// ...
static class ResponseConverter implements Converter<Response>
{
private final Serializer ser = new Persister();
@Override
public Response read(InputNode node) throws Exception
{
Response resp = new Response();
InputNode n = node.getNext();
while( n != null )
{
switch( n.getAttribute("name").getValue() )
{
case "result":
resp.resultTable = ser.read(ResultTable.class, n);
break;
case "response":
resp.responeTable = ser.read(ResponseTable.class, n);
break;
default:
throw new RuntimeException("Unsupported table: "
+ n.getAttribute("name").getValue());
}
n = node.getNext();
}
return resp;
}
@Override
public void write(OutputNode node, Response value) throws Exception
{
// Implement as needed (hint: again use Serializer here)
throw new UnsupportedOperationException("Not supported yet.");
}
}
}
Что здесь происходит:
@Convert
используется для указанияConverter
реализация, которая реализует (де) сериализациюResponse
учебный класс- Реализация
ResponseConverter
используется только для определения типа (= таблицы ответов или результатов)<table …>…</table>
элемент - Для фактической десериализации нормальный
Serializer
используется - нет необходимости делать всю работу вручную!
Я не реализовал write()
часть, но, как вы можете видеть, это не так сложно; Вы можете использовать Serializer
снова сделать основную работу.
Подводя итог Converter
:
- Если таблица с именем атрибута = ответ: десериализовать как
Response
- Если таблица с именем атрибута = результат: десериализовать как
Result
- Остальное: брось исполнение
Это позволяет иметь несколько классов, которые сериализуются в XML, даже если они имеют одно и то же имя элемента.
использование
Есть только одна вещь, чтобы заметить: для @Convert
AnnotationStrategy
должен быть установлен:
Serializer ser = new Persister(new AnnotationStrategy());
// ^^^^^^^^^^^^^^^^^^^^
final String xml = …
Response response = ser.read(Response.class, xml);
System.out.println(response)
Примечание: нет необходимости использовать AnnotationStrategy
стратегия в вашем Converter
- до тех пор, пока вы не полагаетесь на другого Converter
там.
Выход
(генерируется toString()
методы)
Response{resultTable=ResultTable{name=result, a=context, b=name, r=Content{a=stuff1, b=blahblah}}, responeTable=ResponseTable{name=response, a=error, b=reason, r=Content{a=0, b=}}}