Отмена сортировки 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)

Просто для полноты я опубликую решение здесь. Решение, которое я предложил, может быть не общим решением для всех, но может дать некоторые идеи или помочь, если выполняются следующие условия:

  1. Все объекты, которые должны быть введены, "помечаются" аннотацией на уровне класса.
  2. Все объекты, которые должны быть введены, реализуют некоторый интерфейс (хотя это может работать без указания Object?).
  3. Вы довольны созданием собственной пользовательской аннотации параметров.

В моем конкретном случае все мои компоненты помечены аннотацией @Model и реализуют мой интерфейс "Model". Другие классы, такие как репозитории или сервисы, в любом случае могут быть введены в ресурсы джерси путём подсказки. У меня была проблема с аннотацией @InjectParam.

Волшебная часть всего этого, я полагаю, состоит в основном из строки:

    model = request.getEntity(model.getClass());" 

который магическим образом заполняет объект с введенным guice десериализованным содержимым (независимо от того, является ли JSON или XML). Я удивлен, что этого не происходит со встроенным решением при использовании @InjectParam, потому что, в конце концов, фактическое внедрение не было проблемой - оно просто не заполняло объект.

Вот как я это решил:

  1. Создана аннотация под названием "ModelParam":

    @Target({ PARAMETER, METHOD, FIELD })
    @Retention(RUNTIME)
    @Documented
    public @interface ModelParam
    {
    }
    
  2. Заменить аннотацию "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);
    }
    
  3. Создан 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;
        }
    }
    

Надеюсь, это поможет кому-то с подобной проблемой.

С наилучшими пожеланиями, Майкл

Другие вопросы по тегам