All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.xml.XmlFactory Maven / Gradle / Ivy

Go to download

Extension for Jackson (http://jackson.codehaus.org) to offer alternative support for serializing POJOs as XML and deserializing XML as pojos. Support implemented on top of Stax API (javax.xml.stream), by implementing core Jackson Streaming API types like JsonGenerator, JsonParser and JsonFactory. Some data-binding types overridden as well (ObjectMapper sub-classed as XmlMapper).

There is a newer version: 0.6.2
Show newest version
package com.fasterxml.jackson.xml;

import java.io.*;

import javax.xml.stream.*;

import org.codehaus.stax2.io.Stax2ByteArraySource;

import org.codehaus.jackson.*;
import org.codehaus.jackson.format.InputAccessor;
import org.codehaus.jackson.format.MatchStrength;
import org.codehaus.jackson.io.IOContext;

import com.fasterxml.jackson.xml.deser.FromXmlParser;
import com.fasterxml.jackson.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.xml.util.StaxUtil;

/**
* Factory used for constructing {@link FromXmlParser} and {@link ToXmlGenerator}
* instances.
*

* Implements {@link JsonFactory} since interface for constructing XML backed * parsers and generators is quite similar to dealing with JSON. * * @author tatu * * @since 1.6 */ public class XmlFactory extends JsonFactory { /** * Name used to identify JSON format * (and returned by {@link #getFormatName()} */ public final static String FORMAT_NAME_XML = "XML"; /** * Bitfield (set of flags) of all parser features that are enabled * by default. */ final static int DEFAULT_XML_PARSER_FEATURE_FLAGS = FromXmlParser.Feature.collectDefaults(); /** * Bitfield (set of flags) of all generator features that are enabled * by default. */ final static int DEFAULT_XML_GENERATOR_FEATURE_FLAGS = ToXmlGenerator.Feature.collectDefaults(); /* /********************************************************** /* Configuration /********************************************************** */ protected int _xmlParserFeatures = DEFAULT_XML_PARSER_FEATURE_FLAGS; protected int _xmlGeneratorFeatures = DEFAULT_XML_GENERATOR_FEATURE_FLAGS; protected XMLInputFactory _xmlInputFactory; protected XMLOutputFactory _xmlOutputFactory; /* /********************************************************** /* Factory construction, configuration /********************************************************** */ /** * Default constructor used to create factory instances. * Creation of a factory instance is a light-weight operation, * but it is still a good idea to reuse limited number of * factory instances (and quite often just a single instance): * factories are used as context for storing some reused * processing objects (such as symbol tables parsers use) * and this reuse only works within context of a single * factory instance. */ public XmlFactory() { this(null); } public XmlFactory(ObjectCodec oc) { this(oc, null, null); } public XmlFactory(XMLInputFactory xmlIn, XMLOutputFactory xmlOut) { this(null, xmlIn, xmlOut); } public XmlFactory(ObjectCodec oc, XMLInputFactory xmlIn, XMLOutputFactory xmlOut) { super(oc); if (xmlIn == null) { /* 24-Jun-2010, tatu: Ugh. JDK authors seem to waffle on what the name of * factory constructor method is... */ //xmlIn = XMLInputFactory.newFactory(); xmlIn = XMLInputFactory.newInstance(); } if (xmlOut == null) { //xmlOut = XMLOutputFactory.newFactory(); xmlOut = XMLOutputFactory.newInstance(); } // Better ensure namespaces get built properly, so: xmlOut.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE); // and for parser, force coalescing as well (much simpler to use) xmlIn.setProperty(XMLInputFactory.IS_COALESCING, true); _xmlInputFactory = xmlIn; _xmlOutputFactory = xmlOut; } /* /********************************************************** /* Configuration, parser settings /********************************************************** */ /** * Method for enabling or disabling specified XML parser feature. */ public final XmlFactory configure(FromXmlParser.Feature f, boolean state) { if (state) { enable(f); } else { disable(f); } return this; } /** * Method for enabling specified XML parser feature. */ public XmlFactory enable(FromXmlParser.Feature f) { _xmlParserFeatures |= f.getMask(); return this; } /** * Method for disabling specified XML parser feature. */ public XmlFactory disable(FromXmlParser.Feature f) { _xmlParserFeatures &= ~f.getMask(); return this; } /** * Checked whether specified XML parser feature is enabled. */ public final boolean isEnabled(FromXmlParser.Feature f) { return (_xmlParserFeatures & f.getMask()) != 0; } /* /****************************************************** /* Configuration, generator settings /****************************************************** */ /** * Method for enabling or disabling specified XML generator feature. */ public final XmlFactory configure(ToXmlGenerator.Feature f, boolean state) { if (state) { enable(f); } else { disable(f); } return this; } /** * Method for enabling specified XML generator feature. */ public XmlFactory enable(ToXmlGenerator.Feature f) { _xmlGeneratorFeatures |= f.getMask(); return this; } /** * Method for disabling specified XML generator feature. */ public XmlFactory disable(ToXmlGenerator.Feature f) { _xmlGeneratorFeatures &= ~f.getMask(); return this; } /** * Check whether specified XML generator feature is enabled. */ public final boolean isEnabled(ToXmlGenerator.Feature f) { return (_xmlGeneratorFeatures & f.getMask()) != 0; } /* /********************************************************** /* Additional configuration /********************************************************** */ public void setXMLInputFactory(XMLInputFactory f) { _xmlInputFactory = f; } public void setXMLOutputFactory(XMLOutputFactory f) { _xmlOutputFactory = f; } /* /********************************************************** /* Format detection functionality (since 1.8) /********************************************************** */ /** * Method that returns short textual id identifying format * this factory supports. *

* Note: sub-classes should override this method; default * implementation will return null for all sub-classes * * @since 1.8 */ public String getFormatName() { return FORMAT_NAME_XML; } public MatchStrength hasFormat(InputAccessor acc) throws IOException { return hasXMLFormat(acc); } /* /********************************************************** /* Overridden parts of public API /********************************************************** */ /** *

* note: co-variant return type */ @Override public ToXmlGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { // false -> we won't manage the stream unless explicitly directed to IOContext ctxt = _createContext(out, false); return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(out)); } @Override public ToXmlGenerator createJsonGenerator(Writer out) throws IOException { IOContext ctxt = _createContext(out, false); return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(out)); } @Override public ToXmlGenerator createJsonGenerator(File f, JsonEncoding enc) throws IOException { OutputStream out = new FileOutputStream(f); // true -> yes, we have to manage the stream since we created it IOContext ctxt = _createContext(out, true); ctxt.setEncoding(enc); return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, _createXmlWriter(out)); } /* /********************************************************** /* Overridden internal factory methods /********************************************************** */ //protected IOContext _createContext(Object srcRef, boolean resourceManaged) /** * Overridable factory method that actually instantiates desired * parser. */ @Override protected FromXmlParser _createJsonParser(InputStream in, IOContext ctxt) throws IOException, JsonParseException { XMLStreamReader sr; try { sr = _xmlInputFactory.createXMLStreamReader(in); sr = _initializeXmlReader(sr); } catch (XMLStreamException e) { return StaxUtil.throwXmlAsIOException(e); } return new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sr); } /** * Overridable factory method that actually instantiates desired * parser. */ @Override protected FromXmlParser _createJsonParser(Reader r, IOContext ctxt) throws IOException, JsonParseException { XMLStreamReader sr; try { sr = _xmlInputFactory.createXMLStreamReader(r); sr = _initializeXmlReader(sr); } catch (XMLStreamException e) { return StaxUtil.throwXmlAsIOException(e); } return new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sr); } /** * Overridable factory method that actually instantiates desired * parser. */ @Override protected FromXmlParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException, JsonParseException { XMLStreamReader sr; try { sr = _xmlInputFactory.createXMLStreamReader(new Stax2ByteArraySource(data, offset, len)); sr = _initializeXmlReader(sr); } catch (XMLStreamException e) { return StaxUtil.throwXmlAsIOException(e); } return new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sr); } /* /********************************************************************** /* Internal factory methods /********************************************************************** */ protected XMLStreamWriter _createXmlWriter(OutputStream out) throws IOException { try { return _initializeXmlWriter(_xmlOutputFactory.createXMLStreamWriter(out, "UTF-8")); } catch (XMLStreamException e) { return StaxUtil.throwXmlAsIOException(e); } } protected XMLStreamWriter _createXmlWriter(Writer w) throws IOException { try { return _initializeXmlWriter(_xmlOutputFactory.createXMLStreamWriter(w)); } catch (XMLStreamException e) { return StaxUtil.throwXmlAsIOException(e); } } protected final XMLStreamWriter _initializeXmlWriter(XMLStreamWriter sw) throws IOException, XMLStreamException { // And just for Sun Stax parser (JDK default), seems that we better define default namespace // (Woodstox doesn't care) -- otherwise it'll add unnecessary odd declaration sw.setDefaultNamespace(""); return sw; } protected final XMLStreamReader _initializeXmlReader(XMLStreamReader sr) throws IOException, XMLStreamException { // for now, nothing to do... except let's find the root element while (sr.next() != XMLStreamConstants.START_ELEMENT) { ; } return sr; } /* /********************************************************************** /* Internal methods, format auto-detection /********************************************************************** */ private final static byte UTF8_BOM_1 = (byte) 0xEF; private final static byte UTF8_BOM_2 = (byte) 0xBB; private final static byte UTF8_BOM_3 = (byte) 0xBF; private final static byte BYTE_x = (byte) 'x'; private final static byte BYTE_m = (byte) 'm'; private final static byte BYTE_l = (byte) 'l'; private final static byte BYTE_D = (byte) 'D'; private final static byte BYTE_LT = (byte) '<'; private final static byte BYTE_QMARK = (byte) '?'; private final static byte BYTE_EXCL = (byte) '!'; private final static byte BYTE_HYPHEN = (byte) '-'; /** * Method that tries to figure out if content seems to be in some kind * of XML format. * Note that implementation here is not nearly as robust as what underlying * Stax parser will do; the idea is to first support common encodings, * then expand as needed (for example, it is not all that hard to support * UTF-16; but it is some work and not needed quite yet) */ public static MatchStrength hasXMLFormat(InputAccessor acc) throws IOException { /* Basically we just need to find " or , since * can NOT come outside of root */ if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } b = acc.nextByte(); if (b == BYTE_HYPHEN) { if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() == BYTE_HYPHEN) { return MatchStrength.SOLID_MATCH; } } else if (b == BYTE_D) { return tryMatch(acc, "OCTYPE", MatchStrength.SOLID_MATCH); } } else { // maybe root element? Just needs to match first char. if (validXmlNameStartChar(acc, b)) { return MatchStrength.SOLID_MATCH; } } return MatchStrength.NO_MATCH; } private final static boolean validXmlNameStartChar(InputAccessor acc, byte b) throws IOException { /* Can make it actual real XML check in future; for now we do just crude * check for ASCII range */ int ch = (int) b & 0xFF; if (ch >= 'A') { // in theory, colon could be; in practice it should never be valid (wrt namespace) // This is where we'd check for multi-byte UTF-8 chars (or whatever encoding is in use)... return true; } return false; } private final static MatchStrength tryMatch(InputAccessor acc, String matchStr, MatchStrength fullMatchStrength) throws IOException { for (int i = 0, len = matchStr.length(); i < len; ++i) { if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != matchStr.charAt(i)) { return MatchStrength.NO_MATCH; } } return fullMatchStrength; } private final static int skipSpace(InputAccessor acc, byte b) throws IOException { while (true) { int ch = (int) b & 0xFF; if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) { return ch; } if (!acc.hasMoreBytes()) { return -1; } b = acc.nextByte(); ch = (int) b & 0xFF; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy