Отмена сортировки XML или JSON для указания внедренного объекта в джерси с помощью аннотации @InjectParam
У меня возникла проблема с правильной расшифровкой данных XML. Я использую технологии Guice и Jersey. Странно то, что когда я пытаюсь разобрать вручную с помощью JAXB, все работает нормально:
StringBuilder xml = new StringBuilder();
xml.append("<role>");
xml.append(" <name><values><value>Administrator</value><value l=\"en\">Administrator</value></values></name>");
xml.append(" <permissions>");
xml.append(" <permission>READ_XX</permission>");
xml.append(" <permission>WRITE_XX</permission>");
xml.append(" </permissions>");
xml.append("</role>");
JAXBContext jaxbContext = JAXBContext.newInstance(DefaultRole.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Object o = jaxbUnmarshaller.unmarshal(new StringReader(xml.toString()));
Я получаю правильно заполненный объект: DefaultRole [id=null, name=[{val= Администратор}, {l=en, val= Администратор}], права доступа =[READ_XX, WRITE_XX]]
Однако, когда я пытаюсь сделать это с помощью джерси и внедрить мой интерфейс роли, это не работает:
@POST
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public JSONObject processLogin(@InjectParam Role role) throws JSONException
{
System.out.println(role);
return null;
}
Объект создается, но не заполняется: DefaultRole [id=null, name=null, permissions=[]]
Странно то, что когда я заменяю параметр интерфейса (роль роли @InjectParam) классом по умолчанию:
@POST
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public JSONObject processLogin(DefaultRole role) throws JSONException
{
System.out.println(role);
return null;
}
это работает, и я снова правильно заполняю свой объект.
Что мне нужно сделать, чтобы правильно заполнить мой объект при введении его с помощью интерфейса?
Мой класс DefaultRole выглядит так:
@XmlRootElement(name = "role")
@XmlAccessorType(XmlAccessType.FIELD)
public class DefaultRole implements Role
{
private Id id = null;
@XmlJavaTypeAdapter(ContextObjectAdapter.class)
private ContextObject<String> name = null;
@XmlElementWrapper(name = "permissions")
@XmlElement(name = "permission")
private List<String> permissions = Lists.newArrayList();
@Inject
private DefaultRole()
{
}
[...]
Я попытался добавить аннотации JAXB к интерфейсу, но это тоже не помогло. Может кто-нибудь, пожалуйста, пролить свет на это. Я потратил часы, пытаясь заставить это работать сейчас без какой-либо удачи.
Любая помощь по этому вопросу будет принята с благодарностью. Заранее спасибо Майкл
Ответ на вопрос web.xml:
На самом деле я не иду по стандартному маршруту через WEB-INF, но именно так выглядит мой код, который регистрирует компоненты jersey (конечно, если вы все еще хотели бы видеть мой web.xml, я буду рад предоставить его):
Injector injector = ModuleInjector.get().createChildInjector(new JerseyServletModule()
{
@Override
protected void configureServlets()
{
bind(GuiceContainer.class);
Set<Class<?>> foundClasses1 = Reflect.getReflections().getTypesAnnotatedWith(Path.class, false);
Set<Class<?>> foundClasses2 = Reflect.getReflections().getTypesAnnotatedWith(Provider.class, false);
Set<Class<?>> foundClasses = Sets.newHashSet();
foundClasses.addAll(foundClasses1);
foundClasses.addAll(foundClasses2);
if (foundClasses != null && foundClasses.size() > 0)
{
for (Class<?> foundClass : foundClasses)
{
if (foundClass == null)
continue;
if (ResourceConfig.isProviderClass(foundClass)
|| ResourceConfig.isRootResourceClass(foundClass))
{
bind(foundClass);
}
}
}
serve("/restws/*").with(GuiceContainer.class, ImmutableMap.of(JSONConfiguration.FEATURE_POJO_MAPPING, "true"));
}
});
Хорошо, я просто отправлю это. Вот часть моего web.xml:
<!-- WHERE THE JERSEY COMPONENTS ARE BEING REGISTERED -->
<filter>
<display-name>Bootstrap Filter</display-name>
<filter-name>BootstrapFilter</filter-name>
<filter-class>xxx.BootstrapFilter</filter-class>
</filter>
<filter>
<filter-name>Guice Filter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<!-- WHERE THE JERSEY COMPONENTS ARE BEING REGISTERED -->
<filter-mapping>
<filter-name>BootstrapFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>Guice Filter</filter-name>
<url-pattern>/restws/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
1 ответ
После того, как я получил ответ в списке рассылки о том, что джерси не поддерживает guice, я еще немного покопался, чтобы попытаться найти решение. Вот сообщение:
"Джерси 2.0 не поддерживает Guice. См. https://java.net/jira/browse/JERSEY-1950". ( http://jersey.576304.n2.nabble.com/Guice-integration-td7581958.html)
Просто для полноты я опубликую решение здесь. Решение, которое я предложил, может быть не общим решением для всех, но может дать некоторые идеи или помочь, если выполняются следующие условия:
- Все объекты, которые должны быть введены, "помечаются" аннотацией на уровне класса.
- Все объекты, которые должны быть введены, реализуют некоторый интерфейс (хотя это может работать без указания Object?).
- Вы довольны созданием собственной пользовательской аннотации параметров.
В моем конкретном случае все мои компоненты помечены аннотацией @Model и реализуют мой интерфейс "Model". Другие классы, такие как репозитории или сервисы, в любом случае могут быть введены в ресурсы джерси путём подсказки. У меня была проблема с аннотацией @InjectParam.
Волшебная часть всего этого, я полагаю, состоит в основном из строки:
model = request.getEntity(model.getClass());"
который магическим образом заполняет объект с введенным guice десериализованным содержимым (независимо от того, является ли JSON или XML). Я удивлен, что этого не происходит со встроенным решением при использовании @InjectParam, потому что, в конце концов, фактическое внедрение не было проблемой - оно просто не заполняло объект.
Вот как я это решил:
Создана аннотация под названием "ModelParam":
@Target({ PARAMETER, METHOD, FIELD }) @Retention(RUNTIME) @Documented public @interface ModelParam { }
Заменить аннотацию "InjectParam" в моем ресурсе "RoleRestWS" новой аннотацией "ModelParam":
@POST @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public Role createRole(@ModelParam Role role) throws JSONException { return userService.createRole(role); }
Создан InjectableProvider для новой аннотации "ModelParam":
@Provider @Singleton public class ModelInjectableProvider extends AbstractHttpContextInjectable<Model> implements InjectableProvider<ModelParam, Type> { private final Type type; public ModelInjectableProvider() { type = null; } public ModelInjectableProvider(Type type) { this.type = type; } @Override public ComponentScope getScope() { return ComponentScope.Undefined; } @Override public Injectable<Model> getInjectable(ComponentContext ic, ModelParam mp, Type type) { if (type instanceof Class && Model.class.isAssignableFrom((Class<?>) type)) { return new ModelInjectableProvider(type); } return null; } @Override public Model getValue(HttpContext ctx) { if (type instanceof Class && Model.class.isAssignableFrom((Class<?>) type)) { HttpRequestContext request = ctx.getRequest(); Model model = null; if (HttpMethod.POST.equals(request.getMethod()) || HttpMethod.PUT.equals(request.getMethod())) { model = (Model) MyGuiceInjector.inject((Class<?>) type); if (model != null) model = request.getEntity(model.getClass()); } return model; } return null; } }
Надеюсь, это поможет кому-то с подобной проблемой.
С наилучшими пожеланиями, Майкл