Can I validate XML from List<String> without writing to temp file?

198 Views Asked by At

I have XML in a List object that I need to validate against an XSD. So far, I've only been able to validate XML files. As of now, I'm writing my List to a temp file and validating that temp file. I'd really like to eliminate the need for that temp file. My trouble is that javax.xml.validation.Validator.validate expects a Source, but I can't figure out how to get a List into a Source.

Below is my working source that uses the temp file.

     static String validate(List<String> xmlData, Schema schema) throws Exception {
        File tmpFile = File.createTempFile("temp", ".xml");     // TODO: delete
        StringBuilder exceptionList = new StringBuilder();
        
        try {
            Validator validator = schema.newValidator();
            final List<SAXParseException> exceptions = new LinkedList<SAXParseException>();
            validator.setErrorHandler(new ErrorHandler()
            {
              @Override
              public void warning(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }

              @Override
              public void fatalError(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }

              @Override
              public void error(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }
            });

            // TODO: remove this block
            FileWriter fr = new FileWriter(tmpFile);
            for (String str: xmlData) {
                fr.write(str + System.lineSeparator());
            }
            fr.close();
            //
             
            validator.validate(new StreamSource(tmpFile));  // TODO: Here need xmlData instead
            if (! exceptions.isEmpty() ) {
                exceptions.forEach((temp) -> {
                    exceptionList.append(String.format("lineNumber: %s; columnNumber: %s; %s%s", 
                            temp.getLineNumber(),temp.getColumnNumber(),temp.getMessage(),System.lineSeparator()));
                });
            }
            return exceptionList.toString();
        } catch (SAXException | IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (tmpFile.exists()) { tmpFile.delete(); }     // TODO: delete
        }
    }

Edit: For posterity, here's the new code:

    static String validate(String xmlData, Schema schema) throws Exception {
        Reader sourceReader = null;
        StringBuilder exceptionList = new StringBuilder();
        
        try {
            Validator validator = schema.newValidator();
            final List<SAXParseException> exceptions = new LinkedList<SAXParseException>();
            validator.setErrorHandler(new ErrorHandler()
            {
              @Override
              public void warning(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }

              @Override
              public void fatalError(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }

              @Override
              public void error(SAXParseException exception) throws SAXException
              {
                exceptions.add(exception);
              }
            });

            sourceReader = new StringReader(xmlData);
            validator.validate(new StreamSource(sourceReader));
            if (! exceptions.isEmpty() ) {
                exceptions.forEach((temp) -> {
                    exceptionList.append(String.format("lineNumber: %s; columnNumber: %s; %s%s", 
                            temp.getLineNumber(),temp.getColumnNumber(),temp.getMessage(),System.lineSeparator()));
                });
            }
            return exceptionList.toString();
        } catch (SAXException | IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (sourceReader != null) { sourceReader.close(); }
        }
    }
2

There are 2 best solutions below

0
Taylor On BEST ANSWER

You can assemble a String and then wrap that in a Reader like so: https://www.baeldung.com/java-convert-string-to-reader

Then you can use that to create a StreamSource: https://docs.oracle.com/javase/7/docs/api/javax/xml/transform/stream/StreamSource.html#StreamSource(java.io.Reader)

0
Abra On

javadoc is your friend.
StreamSource class has a constructor that takes an InputStream parameter.
ByteArrayInputStream extends InputStream so just create a ByteArrayInputStream from the XML string.

// import java.util.stream.Collectors;

String str = xmlData.stream()
                    .collect(Collectors.joining());
byte[] bytes = str.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
StreamSource source = new StreamSource(bais);

Or, you can use the StreamSource constructor that takes a Reader parameter and create a StringReader from the XML string.

String str = xmlData.stream()
                    .collect(Collectors.joining());
StringReader sr = new StringReader(str);
StreamSource source = new StreamSource(sr);