8

I am trying to capture a ValidationEvent with characters that are not allowed in the XML rules. (e.g, "&") Also, I set up ValidationEventHandler to check for unmarshal errors. I tried to unmarshal using an InputStream, but the event handler does not catch the error. The handleEvent method is not executed at all. On the other hand, using StringReader will work normally.

I've already read the Javadoc that describes the unmarshal method. However, I did not see that it could not capture the ValidationEvent.

Unmarshal XML data from the specified InputStream and return the resulting content tree. Validation event location information may be incomplete when using this form of the unmarshal API.

On the last attempt, I did try searching online but I couldn't find anything.
Any help will be appreciated :D

Extra question:

I am sorry to add a question. (The POJO class has changed a bit ...) I defined the POJO class field with @XmlPath Annotation differently from the XML element name, but it does not seem to work. Should I use it as an XmlElement?


POJO class:

import org.eclipse.persistence.oxm.annotations.XmlPath;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;

@XmlRootElement
class Article {
    private String title;
    private String category;
    private List<ArticleImage> imageList;

    public String getTitle() {
        return title;
    }

    @XmlElement
    public void setTitle(String title) {
        this.title = title;
    }

    public String getCategory() {
        return category;
    }

    @XmlElement
    public void setCategory(String category) {
        this.category = category;
    }

    public List<ArticleImage> getImageList() {
        return imageList;
    }

    // for Extra Question... :D
    @XmlPath("image")
    public void setImageList(List<ArticleImage> imageList) {
        this.imageList = imageList;
    }
}

class ArticleImage {
    private String url;
    private String ext;

    public String getUrl() {
        return url;
    }

    @XmlAttribute
    public void setUrl(String url) {
        this.url = url;
    }

    public String getExt() {
        return ext;
    }

    @XmlAttribute
    public void setExt(String ext) {
        this.ext = ext;
    }
}


ValidationEventHandler:

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;

class CustomValidationHandler implements ValidationEventHandler {

    // This method is not reached when debugging.
    @Override
    public boolean handleEvent(ValidationEvent event) {
        if (event.getSeverity() == ValidationEvent.FATAL_ERROR ||
                event.getSeverity() == ValidationEvent.ERROR) {
            ValidationEventLocator locator = event.getLocator();
            throw new RuntimeException("Error in EventHandler. line number: " + locator.getLineNumber());
        }
        return true;
    }
}


Unmarshal code:

import org.eclipse.persistence.jaxb.JAXBContextFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class SomeTest {

    public void someMethod() {
        String xmlString = "<article type=\"item\">\n"
                + "    <title>M&A</title>\n"
                + "    <category>1234-1234</category>\n"
                + "    <image url=\"hello\" ext=\"jpg\"/>\n"
                + "</article>";

        try (InputStream fileInputStream = new ByteArrayInputStream(xmlString.getBytes())) {
            JAXBContext context = JAXBContextFactory.createContext(new Class[]{Article.class}, null);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            unmarshaller.setEventHandler(new CustomValidationHandler());
            Article article = (Article) unmarshaller.unmarshal(fileInputStream);

            System.out.println(article.getTitle());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new SomeTest().someMethod();
    }
}
Madplay
  • 1,027
  • 1
  • 13
  • 25
  • 1
    Could you try replacing `JAXBContext context = JAXBContextFactory.createContext(new Class[]{Article.class}, null);` with `JAXBContext context = JAXBContext.newInstance(Article.class);` and see if it still does not get triggered? – martidis May 21 '19 at 10:50
  • @mart still getting the error :( – Madplay May 21 '19 at 14:28
  • you get the error, but is your eventhandler being used? I believe it is. – martidis May 21 '19 at 14:29
  • Oh, I mean "getting another error: ". ```Class has two properties of the same name "title"``` I think I need to clear the setter or reposition the annotation. – Madplay May 21 '19 at 14:41
  • 1
    It's because of your combination of `@XmlAccessorType` and where you keep your `@XmlElement` (on field or getter/setter). Check this for more info https://stackoverflow.com/questions/48888250/what-is-the-difference-between-using-xmlelement-before-field-and-before-getter/48888373#48888373 – martidis May 21 '19 at 14:42
  • 1
    Thank you for good information! It works well in the POJO class before modification.(I'm sorry to ask more questions...) – Madplay May 21 '19 at 15:27
  • 1
    I need to find the difference between ```newInstance``` and ```createContext``` Thanks! (I do not see your answer in the answer list t:D ) – Madplay May 21 '19 at 15:30
  • I can put my input there as well :) do you still have remaining questions/issues? – martidis May 21 '19 at 15:39
  • 1
    That's very kind of you! I edited my question.(Maybe it's a problem using different packages) If you do not mind, please add your answer to answer list :D – Madplay May 21 '19 at 16:06
  • I ll be afk in a bit. I will be back online in couple of hours and have a look :) – martidis May 21 '19 at 16:09
  • If you have something to do, I'm really fine even if you do not answer me :D – Madplay May 21 '19 at 17:00
  • Updated the answer :) Let me know if the question is about something different. – martidis May 21 '19 at 18:16

3 Answers3

9

Initially suggested to replace

JAXBContext context = JAXBContextFactory.createContext(new Class[]{Article.class}, null);

to

JAXBContext context = JAXBContext.newInstance(Article.class);

In the implementation of JAXBContextFactory.createContext you can see that the classesToBeBound, which in your case you pass Article, perform some checks which result in exception for being "Unable to find a JAXB implementation to delegate". This is the problem I had when running your code and I assumed you had as well. Then your event handler was called (at least in my setup).

Later you had issues with "... Class has two properties of the same name "value" ..." and I proposed to check this link which explains the reason it happens.

the link is: What is the difference between using @XmlElement before field and before getter declaration?

Edit to reply to new question:

If you don't mind me asking what are you trying to do with @XmlPath("image")?

Your POJO structure does not match the xml. Remove the annotation and change the setter method for image, as below:

@XmlRootElement
class Article {
    private String title;
    private String category;
    private List<ArticleImage> imageList;

    public String getTitle() {
        return title;
    }

    @XmlElement
    public void setTitle(String title) {
        this.title = title;
    }

    public String getCategory() {
        return category;
    }

    @XmlElement
    public void setCategory(String category) {
        this.category = category;
    }

    public List<ArticleImage> getImage() {
        return imageList;
    }

    // for Extra Question... :D
    // method name changed!
    public void setImage(List<ArticleImage> imageList) {
        this.imageList = imageList;
    }
}

class ArticleImage {
    private String url;
    private String ext;

    public String getUrl() {
        return url;
    }

    @XmlAttribute
    public void setUrl(String url) {
        this.url = url;
    }

    public String getExt() {
        return ext;
    }

    @XmlAttribute
    public void setExt(String ext) {
        this.ext = ext;
    }
}
martidis
  • 2,897
  • 1
  • 11
  • 13
3

As told by @mart and @Sergey

Update JAXBContext context = JAXBContextFactory.createContext(new Class[]{Article.class}, null);

   JAXBContext context = JAXBContext.newInstance(Article.class);

And Also Add @XmlAccessorType(XmlAccessType.FIELD) in your Article pojo class.

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Article {
    @XmlElement
    private String title;
    @XmlElement
    private String category;

//setter getters

}

I have checked it,In case of exception, CustomValidationHandler is being called.

2

it works for both cases: InputStream and StringReader

just changed that:

JAXBContext context = JAXBContext.newInstance(Article.class);