JAXB unmarshaller's handleEvent not being called

Viewed 373

I'm trying to setup a simple Jersey JAX-RS application using MOXy for JAXB JSON support and I wanted to customise the unmarshaller. I did the following:

@Provider
public class CustomProvider extends ConfigurableMoxyJsonProvider{
  @Override
  protected void preReadFrom(..., Unmarshaller unmarshaller) throws JAXBException{
    super.preReadFrom(...);
    System.out.println("preReadFrom entered");

    unmarshaller.setEventHandler(new ValidationEventHandler(){
      @Override
      public boolean handleEvent(ValidationEvent event){
        System.out.println("Entered handleEvent");

        return false;
      }
    });
  }
}

I wrote an override for preReadFrom and set an event handler on the unmarshaller. When I pass an invalid JSON body, print statement in preReadFrom executes but not the one in event handler. So the provider is registered properly but the event handler is not being called.

What might cause this issue?

What I want to achieve is when a user passes extraneous attributes in the JSON body, I want to throw an error (By default, these attributes are ignored). Searching on various websites, adding an event handler is the only way to do that. It would be great if I can achieve this in a different way too.

1 Answers
  1. I assume System.out.println("preReadFrom entered"); is getting called, and your CustomProvider is actually registered and used. Because in Weblogic e.g even if you register a different provider, still default ConfigurableMoxyJsonProvider is getting called unless you disable Moxy.

  2. If first assumption is correct, then for sure you will be getting public boolean handleEvent(ValidationEvent event) called for validations like if your Pojo, attribute is of numeric and you are passing String in json.

  3. For UnmappedElements, I have seen that Moxy, "ignores" the warning for Json. What that means is if your pojo is like below:

    public class Emp {
        public Emp() {
            super();
        }
        private int id ;
        public void setId(int id) {
            this.id = id;
        }
    
        public int getId() {
            return id;
        }
    
    }
    

    And your Json is like: {"id" : 11, "name1" : 111} then your handleEvent won't be called. Reason being the Moxy uses org.eclipse.persistence.internal.oxm.record.UnmarshalRecordI‌mpl.startUnmappedEle‌​ment() that has a check to see if the media type is xml prior to issuing a warning to the event handler.

So how to solve it for throw. I may not know the best of answer but here is my solution :

In your preReadFrom method add your custom UnmappedHandlerClass like below

protected void preReadFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
                           MultivaluedMap<String, String> httpHeaders,
                           Unmarshaller unmarshaller) throws JAXBException {
    System.out.println("preReadFrom entered");
    super.preReadFrom(type, genericType, annotations, MediaType.WILDCARD_TYPE, httpHeaders, unmarshaller);
    System.out.println("preReadFrom returned from super");

    //new code
    if(unmarshaller instanceof org.eclipse.persistence.jaxb.JAXBUnmarshaller) {
        org.eclipse.persistence.jaxb.JAXBUnmarshaller   moxyUn = ( org.eclipse.persistence.jaxb.JAXBUnmarshaller)unmarshaller;
        moxyUn.getXMLUnmarshaller().setUnmappedContentHandlerClass(CustomUnmapped.class);
    }
//your existing code
    unmarshaller.setEventHandler(new ValidationEventHandler() {
        @Override
        public boolean handleEvent(ValidationEvent event) {
            System.out.println("Entered handleEvent");

            return false;
        }
    });
}

And you can use Customunmapped class like this:

class CustomUnmapped extends org.eclipse.persistence.internal.oxm.unmapped.DefaultUnmappedContentHandler {
    @Override
    public void startElement(String p1, String p2, String p3, Attributes p4) throws SAXException {
        throw new SAXNotRecognizedException(p1);
    }
}

That shall work. But before trying 3, just make sure that your a) CustomProvider is actually being called by adding a breakpoint, or System.out.. statement. and b) Your handleEvent is called for generic errors like passing a string x in json, for number field.

Related