com.microsoft.windowsazure.storage.table.TableParser.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.windowsazure.storage.table.TableParser.java

Source

/**
 * Copyright Microsoft Corporation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.microsoft.windowsazure.storage.table;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.lang3.StringEscapeUtils;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.microsoft.windowsazure.storage.Constants;
import com.microsoft.windowsazure.storage.OperationContext;
import com.microsoft.windowsazure.storage.StorageErrorCodeStrings;
import com.microsoft.windowsazure.storage.StorageException;
import com.microsoft.windowsazure.storage.core.SR;
import com.microsoft.windowsazure.storage.core.Utility;

/**
 * Reserved for internal use. A class used to read and write Table entities in OData AtomPub format requests and
 * responses.
 * <p>
 * For more information about OData, see the <a href="http://www.odata.org/">Open Data Protocol</a> website. For more
 * information about the AtomPub format used in OData, see <a
 * href="http://www.odata.org/developers/protocols/atom-format">OData Protocol Atom Format</a>.
 */
class TableParser {

    /**
     * Used to create Json parsers and generators.
     */
    private static JsonFactory jsonFactory = new JsonFactory();

    /**
     * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the
     * specified input stream using the specified class type and optionally projects each entity result with the
     * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. .
     * 
     * @param inStream
     *            The <code>InputStream</code> to read the data to parse from.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entities returned. Set to
     *            <code>null</code> to ignore the returned entities and copy only response properties into the
     *            {@link TableResult} objects.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entities into instances of type <code>R</code>. Set
     *            to <code>null</code> to return the entities as instances of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation
     *         response.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream with AtomPub.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws StorageException
     *             if a storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream with Json.
     * @throws JsonParseException
     *             if an error occurs while parsing the stream.
     */
    protected static <T extends TableEntity, R> ODataPayload<?> parseQueryResponse(final InputStream inStream,
            final TableRequestOptions options, final Class<T> clazzType, final EntityResolver<R> resolver,
            final OperationContext opContext) throws InstantiationException, IllegalAccessException,
            XMLStreamException, ParseException, StorageException, JsonParseException, IOException {
        ODataPayload<?> payload;
        if (options.getTablePayloadFormat() == TablePayloadFormat.AtomPub) {
            payload = parseAtomQueryResponse(inStream, clazzType, resolver, opContext);
        } else {
            payload = parseJsonQueryResponse(inStream, clazzType, resolver, options, opContext);
        }
        return payload;
    }

    /**
     * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the
     * specified input stream using the specified class type and optionally projects each entity result with the
     * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. .
     * 
     * @param inStream
     *            The <code>InputStream</code> to read the data to parse from.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param httpStatusCode
     *            The HTTP status code returned with the operation response.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entities returned. Set to
     *            <code>null</code> to ignore the returned entities and copy only response properties into the
     *            {@link TableResult} objects.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entities into instances of type <code>R</code>. Set
     *            to <code>null</code> to return the entities as instances of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         A {@link TableResult} object with the parsed operation response.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream with AtomPub.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws StorageException
     *             if a storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream with Json.
     * @throws JsonParseException
     *             if an error occurs while parsing the stream.
     */
    protected static <T extends TableEntity, R> TableResult parseSingleOpResponse(final InputStream inStream,
            final TableRequestOptions options, final int httpStatusCode, final Class<T> clazzType,
            final EntityResolver<R> resolver, final OperationContext opContext)
            throws InstantiationException, IllegalAccessException, XMLStreamException, ParseException,
            StorageException, IOException, JsonParseException {
        TableResult res;
        if (options.getTablePayloadFormat() == TablePayloadFormat.AtomPub) {
            res = parseSingleOpAtomResponse(inStream, httpStatusCode, clazzType, resolver, opContext);
        } else {
            res = parseSingleOpJsonResponse(inStream, httpStatusCode, clazzType, resolver, options, opContext);
        }
        return res;
    }

    /**
     * Reserved for internal use. Writes an entity to the stream, leaving the stream open for additional writing.
     * 
     * @param outStream
     *            The <code>OutputStream</code> to write the entity to.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream with AtomPub.
     * @throws StorageException
     *             if a Storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream with Json.
     */
    protected static void writeSingleEntityToStream(final OutputStream outStream, final TablePayloadFormat format,
            final TableEntity entity, final boolean isTableEntry, final OperationContext opContext)
            throws XMLStreamException, StorageException, IOException {
        if (format == TablePayloadFormat.AtomPub) {
            writeSingleAtomEntity(outStream, entity, isTableEntry, opContext);
        } else {
            writeSingleJsonEntity(outStream, format, entity, isTableEntry, opContext);
        }
    }

    /**
     * Reserved for internal use. Writes an entity to the stream, leaving the stream open for additional writing.
     * 
     * @param strWriter
     *            The <code>StringWriter</code> to write the entity to.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream with AtomPub.
     * @throws StorageException
     *             if a Storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream with Json.
     */
    protected static void writeSingleEntityToString(final StringWriter strWriter, final TablePayloadFormat format,
            final TableEntity entity, final boolean isTableEntry, final OperationContext opContext)
            throws XMLStreamException, StorageException, IOException {
        if (format == TablePayloadFormat.AtomPub) {
            writeSingleAtomEntity(strWriter, entity, isTableEntry, opContext);
        } else {
            writeSingleJsonEntity(strWriter, format, entity, isTableEntry, opContext);
        }
    }

    /**
     * Reserved for internal use. Parses the operation response as an entity. Parses the result returned in the
     * specified stream in JSON format into a {@link TableResult} containing an entity of the specified class type
     * projected using the specified resolver.
     * 
     * @param parser
     *            The <code>JsonParser</code> to read the data to parse from.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entity returned. Set to
     *            <code>null</code> to ignore the returned entity and copy only response properties into the
     *            {@link TableResult} object.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entity into an instance of type <code>R</code>. Set
     *            to <code>null</code> to return the entity as an instance of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         A {@link TableResult} containing the parsed entity result of the operation.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     * @throws JsonParseException
     *             if an error occurs while parsing the stream.
     */
    private static <T extends TableEntity, R> TableResult parseJsonEntity(final JsonParser parser,
            final Class<T> clazzType, HashMap<String, PropertyPair> classProperties,
            final EntityResolver<R> resolver, final TableRequestOptions options, final OperationContext opContext)
            throws JsonParseException, IOException, ParseException, StorageException, InstantiationException,
            IllegalAccessException {
        final TableResult res = new TableResult();

        final HashMap<String, EntityProperty> properties = new HashMap<String, EntityProperty>();

        if (!parser.hasCurrentToken()) {
            parser.nextToken();
        }

        ODataUtilities.assertIsStartObjectJsonToken(parser);

        parser.nextToken();

        // get all metadata, if present
        while (parser.getCurrentName().startsWith(ODataConstants.ODATA_PREFIX)) {
            final String name = parser.getCurrentName().substring(ODataConstants.ODATA_PREFIX.length());

            // get the value token
            parser.nextToken();

            if (name.equals(ODataConstants.ETAG)) {
                String etag = parser.getValueAsString();
                res.setEtag(etag);
            }

            // get the key token
            parser.nextToken();
        }

        if (resolver == null && clazzType == null) {
            return res;
        }

        // get object properties
        while (parser.getCurrentToken() != JsonToken.END_OBJECT) {
            String key = Constants.EMPTY_STRING;
            String val = Constants.EMPTY_STRING;
            EdmType edmType = null;

            // checks if this property is preceded by an OData property type annotation
            if (options.getTablePayloadFormat() != TablePayloadFormat.JsonNoMetadata
                    && parser.getCurrentName().endsWith(ODataConstants.ODATA_TYPE_SUFFIX)) {
                parser.nextToken();
                edmType = EdmType.parse(parser.getValueAsString());

                parser.nextValue();
                key = parser.getCurrentName();
                val = parser.getValueAsString();
            } else {
                key = parser.getCurrentName();

                parser.nextToken();
                val = parser.getValueAsString();
                edmType = evaluateEdmType(parser.getCurrentToken(), parser.getValueAsString());
            }

            final EntityProperty newProp = new EntityProperty(val, edmType);
            properties.put(key, newProp);

            parser.nextToken();
        }

        String partitionKey = null;
        String rowKey = null;
        Date timestamp = null;
        String etag = null;

        // Remove core properties from map and set individually
        EntityProperty tempProp = properties.remove(TableConstants.PARTITION_KEY);
        if (tempProp != null) {
            partitionKey = tempProp.getValueAsString();
        }

        tempProp = properties.remove(TableConstants.ROW_KEY);
        if (tempProp != null) {
            rowKey = tempProp.getValueAsString();
        }

        tempProp = properties.remove(TableConstants.TIMESTAMP);
        if (tempProp != null) {
            timestamp = tempProp.getValueAsDate();

            if (res.getEtag() == null) {
                etag = getETagFromTimestamp(tempProp.getValueAsString());
                res.setEtag(etag);
            }
        }

        // do further processing for type if JsonNoMetdata by inferring type information via resolver or clazzType
        if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata
                && (options.getPropertyResolver() != null || clazzType != null)) {
            if (options.getPropertyResolver() != null) {
                for (final Entry<String, EntityProperty> p : properties.entrySet()) {
                    final String key = p.getKey();
                    final String value = p.getValue().getValueAsString();
                    EdmType edmType;

                    // try to use the property resolver to get the type
                    try {
                        edmType = options.getPropertyResolver().propertyResolver(partitionKey, rowKey, key, value);
                    } catch (Exception e) {
                        throw new StorageException(StorageErrorCodeStrings.INTERNAL_ERROR, SR.CUSTOM_RESOLVER_THREW,
                                Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
                    }

                    // try to create a new entity property using the returned type
                    try {
                        final EntityProperty newProp = new EntityProperty(value, edmType);
                        properties.put(p.getKey(), newProp);
                    } catch (IllegalArgumentException e) {
                        throw new StorageException(StorageErrorCodeStrings.INVALID_TYPE,
                                String.format(SR.FAILED_TO_PARSE_PROPERTY, key, value, edmType),
                                Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
                    }
                }
            } else if (clazzType != null) {
                if (classProperties == null) {
                    classProperties = PropertyPair.generatePropertyPairs(clazzType);
                }
                for (final Entry<String, EntityProperty> p : properties.entrySet()) {
                    PropertyPair propPair = classProperties.get(p.getKey());
                    if (propPair != null) {
                        final EntityProperty newProp = new EntityProperty(p.getValue().getValueAsString(),
                                propPair.type);
                        properties.put(p.getKey(), newProp);
                    }
                }
            }
        }

        // set the result properties, now that they are appropriately parsed
        res.setProperties(properties);

        // use resolver if provided, else create entity based on clazz type
        if (resolver != null) {
            res.setResult(resolver.resolve(partitionKey, rowKey, timestamp, res.getProperties(), res.getEtag()));
        } else if (clazzType != null) {
            // Generate new entity and return
            final T entity = clazzType.newInstance();
            entity.setEtag(res.getEtag());

            entity.setPartitionKey(partitionKey);
            entity.setRowKey(rowKey);
            entity.setTimestamp(timestamp);

            entity.readEntity(res.getProperties(), opContext);

            res.setResult(entity);
        }

        return res;
    }

    /**
     * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the
     * specified input stream using the specified class type and optionally projects each entity result with the
     * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects.
     * 
     * @param inStream
     *            The <code>InputStream</code> to read the data to parse from.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entities returned. Set to
     *            <code>null</code> to ignore the returned entities and copy only response properties into the
     *            {@link TableResult} objects.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entities into instances of type <code>R</code>. Set
     *            to <code>null</code> to return the entities as instances of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation
     *         response.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     * @throws JsonParseException
     *             if an error occurs while parsing the stream.
     */
    @SuppressWarnings("unchecked")
    private static <T extends TableEntity, R> ODataPayload<?> parseJsonQueryResponse(final InputStream inStream,
            final Class<T> clazzType, final EntityResolver<R> resolver, final TableRequestOptions options,
            final OperationContext opContext) throws ParseException, InstantiationException, IllegalAccessException,
            StorageException, JsonParseException, IOException {
        ODataPayload<T> corePayload = null;
        ODataPayload<R> resolvedPayload = null;
        ODataPayload<?> commonPayload = null;

        JsonParser parser = createJsonParserFromStream(inStream);

        try {

            if (resolver != null) {
                resolvedPayload = new ODataPayload<R>();
                commonPayload = resolvedPayload;
            } else {
                corePayload = new ODataPayload<T>();
                commonPayload = corePayload;
            }

            if (!parser.hasCurrentToken()) {
                parser.nextToken();
            }

            ODataUtilities.assertIsStartObjectJsonToken(parser);

            // move into data  
            parser.nextToken();

            // if there is a clazz type and if JsonNoMetadata, create a classProperties dictionary to use for type inference once 
            // instead of querying the cache many times
            HashMap<String, PropertyPair> classProperties = null;
            if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata && clazzType != null) {
                classProperties = PropertyPair.generatePropertyPairs(clazzType);
            }

            while (parser.getCurrentToken() != null) {
                if (parser.getCurrentToken() == JsonToken.FIELD_NAME
                        && parser.getCurrentName().equals(ODataConstants.VALUE)) {
                    // move to start of array
                    parser.nextToken();

                    ODataUtilities.assertIsStartArrayJsonToken(parser);

                    // go to properties
                    parser.nextToken();

                    while (parser.getCurrentToken() == JsonToken.START_OBJECT) {
                        final TableResult res = parseJsonEntity(parser, clazzType, classProperties, resolver,
                                options, opContext);
                        if (corePayload != null) {
                            corePayload.tableResults.add(res);
                        }

                        if (resolver != null) {
                            resolvedPayload.results.add((R) res.getResult());
                        } else {
                            corePayload.results.add((T) res.getResult());
                        }

                        parser.nextToken();
                    }

                    ODataUtilities.assertIsEndArrayJsonToken(parser);
                }

                parser.nextToken();
            }
        } finally {
            parser.close();
        }

        return commonPayload;
    }

    /**
     * Reserved for internal use. Parses the operation response as an entity. Reads entity data from the specified
     * <code>JsonParser</code> using the specified class type and optionally projects the entity result with the
     * specified resolver into a {@link TableResult} object.
     * 
     * @param parser
     *            The <code>JsonParser</code> to read the data to parse from.
     * @param httpStatusCode
     *            The HTTP status code returned with the operation response.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entity returned. Set to
     *            <code>null</code> to ignore the returned entity and copy only response properties into the
     *            {@link TableResult} object.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entity into an instance of type <code>R</code>. Set
     *            to <code>null</code> to return the entitys as instance of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         A {@link TableResult} object with the parsed operation response.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     * @throws JsonParseException
     *             if an error occurs while parsing the stream.
     */
    private static <T extends TableEntity, R> TableResult parseSingleOpJsonResponse(final InputStream inStream,
            final int httpStatusCode, final Class<T> clazzType, final EntityResolver<R> resolver,
            final TableRequestOptions options, final OperationContext opContext) throws ParseException,
            InstantiationException, IllegalAccessException, StorageException, JsonParseException, IOException {
        JsonParser parser = createJsonParserFromStream(inStream);

        try {
            final TableResult res = parseJsonEntity(parser, clazzType,
                    null /*HashMap<String, PropertyPair> classProperties*/, resolver, options, opContext);
            res.setHttpStatusCode(httpStatusCode);
            return res;
        } finally {
            parser.close();
        }
    }

    /**
     * Reserved for internal use. Writes an entity to the specified <code>JsonGenerator</code> as an JSON resource
     * 
     * @param generator
     *            The <code>JsonGenerator</code> to write the entity to.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * 
     * @throws StorageException
     *             if a Storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     */
    private static void writeJsonEntity(final JsonGenerator generator, TablePayloadFormat format,
            final TableEntity entity, final boolean isTableEntry, final OperationContext opContext)
            throws StorageException, IOException {

        HashMap<String, EntityProperty> properties = entity.writeEntity(opContext);
        if (properties == null) {
            properties = new HashMap<String, EntityProperty>();
        }

        // start object
        generator.writeStartObject();

        if (!isTableEntry) {
            Utility.assertNotNull(TableConstants.PARTITION_KEY, entity.getPartitionKey());
            Utility.assertNotNull(TableConstants.ROW_KEY, entity.getRowKey());
            Utility.assertNotNull(TableConstants.TIMESTAMP, entity.getTimestamp());

            // PartitionKey
            generator.writeStringField(TableConstants.PARTITION_KEY, entity.getPartitionKey());

            // RowKey
            generator.writeStringField(TableConstants.ROW_KEY, entity.getRowKey());

            // Timestamp
            generator.writeStringField(TableConstants.TIMESTAMP, Utility
                    .getTimeByZoneAndFormat(entity.getTimestamp(), Utility.UTC_ZONE, Utility.ISO8061_LONG_PATTERN));
        }

        for (final Entry<String, EntityProperty> ent : properties.entrySet()) {
            if (ent.getKey().equals(TableConstants.PARTITION_KEY) || ent.getKey().equals(TableConstants.ROW_KEY)
                    || ent.getKey().equals(TableConstants.TIMESTAMP) || ent.getKey().equals("Etag")) {
                continue;
            }

            EntityProperty currProp = ent.getValue();
            if (currProp.getEdmType().mustAnnotateType()) {
                final String edmTypeString = currProp.getEdmType().toString();

                // property type
                generator.writeStringField(ent.getKey() + ODataConstants.ODATA_TYPE_SUFFIX, edmTypeString);

                // property key and value
                generator.writeStringField(ent.getKey(), ent.getValue().getValueAsString());
            } else if (currProp.getEdmType() == EdmType.DOUBLE && currProp.getIsNull() == false) {
                final String edmTypeString = currProp.getEdmType().toString();
                final Double value = currProp.getValueAsDouble();

                // property type, if needed
                if (value.equals(Double.POSITIVE_INFINITY) || value.equals(Double.NEGATIVE_INFINITY)
                        || value.equals(Double.NaN)) {
                    generator.writeStringField(ent.getKey() + ODataConstants.ODATA_TYPE_SUFFIX, edmTypeString);

                    // property key and value
                    generator.writeStringField(ent.getKey(), ent.getValue().getValueAsString());
                } else {
                    writeJsonProperty(generator, ent);
                }

            } else {
                writeJsonProperty(generator, ent);
            }
        }

        // end object
        generator.writeEndObject();
    }

    /**
     * Reserved for internal use. Writes an entity to the stream as an JSON resource, leaving the stream open
     * for additional writing.
     * 
     * @param outStream
     *            The <code>OutputStream</code> to write the entity to.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * 
     * @throws StorageException
     *             if a Storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     */
    private static void writeSingleJsonEntity(final OutputStream outStream, TablePayloadFormat format,
            final TableEntity entity, final boolean isTableEntry, final OperationContext opContext)
            throws StorageException, IOException {
        JsonGenerator generator = jsonFactory.createGenerator(outStream);

        try {
            // write to stream
            writeJsonEntity(generator, format, entity, isTableEntry, opContext);
        } finally {
            generator.close();
        }
    }

    /**
     * Reserved for internal use. Writes an entity to the stream as an JSON resource, leaving the stream open
     * for additional writing.
     * 
     * @param strWriter
     *            The <code>StringWriter</code> to write the entity to.
     * @param format
     *            The {@link TablePayloadFormat} to use for parsing.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * 
     * @throws StorageException
     *             if a Storage service error occurs.
     * @throws IOException
     *             if an error occurs while accessing the stream.
     */
    private static void writeSingleJsonEntity(final StringWriter strWriter, TablePayloadFormat format,
            final TableEntity entity, final boolean isTableEntry, final OperationContext opContext)
            throws StorageException, IOException {
        JsonGenerator generator = jsonFactory.createGenerator(strWriter);

        try {
            // write to stream
            writeJsonEntity(generator, format, entity, isTableEntry, opContext);
        } finally {
            generator.close();
        }
    }

    /**
     * Reserved for internal use. Parses the operation response as an entity. Parses the result returned in the
     * specified stream in AtomPub format into a {@link TableResult} containing an entity of the specified class type
     * projected using the specified resolver.
     * 
     * @param xmlr
     *            An <code>XMLStreamReader</code> on the input stream.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entity returned. Set to
     *            <code>null</code> to ignore the returned entity and copy only response properties into the
     *            {@link TableResult} object.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entity into an instance of type <code>R</code>. Set
     *            to <code>null</code> to return the entity as an instance of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         A {@link TableResult} containing the parsed entity result of the operation.
     * 
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     */
    private static <T extends TableEntity, R> TableResult parseAtomEntity(final XMLStreamReader xmlr,
            final Class<T> clazzType, final EntityResolver<R> resolver, final OperationContext opContext)
            throws XMLStreamException, ParseException, InstantiationException, IllegalAccessException,
            StorageException {
        int eventType = xmlr.getEventType();
        final TableResult res = new TableResult();

        xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.ENTRY);

        String etag = StringEscapeUtils.unescapeHtml4(
                xmlr.getAttributeValue(ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.ETAG));

        res.setEtag(etag);

        while (xmlr.hasNext()) {
            eventType = xmlr.next();
            if (eventType == XMLStreamConstants.CHARACTERS) {
                xmlr.getText();
                continue;
            }

            final String name = xmlr.getName().toString();

            if (eventType == XMLStreamConstants.START_ELEMENT) {
                if (name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.ID)) {
                    Utility.readElementFromXMLReader(xmlr, ODataConstants.ID);
                } else if (name
                        .equals(ODataConstants.BRACKETED_DATA_SERVICES_METADATA_NS + ODataConstants.PROPERTIES)) {
                    // Do read properties
                    if (resolver == null && clazzType == null) {
                        return res;
                    } else {
                        res.setProperties(readAtomProperties(xmlr, opContext));
                        break;
                    }
                }
            }
        }

        // Move to end Content
        eventType = xmlr.next();
        if (eventType == XMLStreamConstants.CHARACTERS) {
            eventType = xmlr.next();
        }

        xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.CONTENT);

        eventType = xmlr.next();
        if (eventType == XMLStreamConstants.CHARACTERS) {
            eventType = xmlr.next();
        }

        xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.ENTRY);

        String rowKey = null;
        String partitionKey = null;
        Date timestamp = null;

        // Remove core properties from map and set individually
        EntityProperty tempProp = res.getProperties().remove(TableConstants.PARTITION_KEY);
        if (tempProp != null) {
            partitionKey = tempProp.getValueAsString();
        }

        tempProp = res.getProperties().remove(TableConstants.ROW_KEY);
        if (tempProp != null) {
            rowKey = tempProp.getValueAsString();
        }

        tempProp = res.getProperties().remove(TableConstants.TIMESTAMP);
        if (tempProp != null) {
            timestamp = tempProp.getValueAsDate();
        }

        if (resolver != null) {
            // Call resolver
            res.setResult(resolver.resolve(partitionKey, rowKey, timestamp, res.getProperties(), res.getEtag()));
        } else if (clazzType != null) {
            // Generate new entity and return
            final T entity = clazzType.newInstance();
            entity.setEtag(res.getEtag());

            entity.setPartitionKey(partitionKey);
            entity.setRowKey(rowKey);
            entity.setTimestamp(timestamp);

            entity.readEntity(res.getProperties(), opContext);

            res.setResult(entity);
        }

        return res;
    }

    /**
     * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the
     * specified input stream using the specified class type and optionally projects each entity result with the
     * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects.
     * 
     * @param inStream
     *            The <code>InputStream</code> to read the data to parse from.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entities returned. Set to
     *            <code>null</code> to ignore the returned entities and copy only response properties into the
     *            {@link TableResult} objects.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entities into instances of type <code>R</code>. Set
     *            to <code>null</code> to return the entities as instances of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation
     *         response.
     * 
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     */
    @SuppressWarnings("unchecked")
    private static <T extends TableEntity, R> ODataPayload<?> parseAtomQueryResponse(final InputStream inStream,
            final Class<T> clazzType, final EntityResolver<R> resolver, final OperationContext opContext)
            throws XMLStreamException, ParseException, InstantiationException, IllegalAccessException,
            StorageException {
        ODataPayload<T> corePayload = null;
        ODataPayload<R> resolvedPayload = null;
        ODataPayload<?> commonPayload = null;

        if (resolver != null) {
            resolvedPayload = new ODataPayload<R>();
            commonPayload = resolvedPayload;
        } else {
            corePayload = new ODataPayload<T>();
            commonPayload = corePayload;
        }

        final XMLStreamReader xmlr = Utility.createXMLStreamReaderFromStream(inStream);
        int eventType = xmlr.getEventType();
        xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null);
        eventType = xmlr.next();

        xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.FEED);
        // skip feed chars
        eventType = xmlr.next();

        while (xmlr.hasNext()) {
            eventType = xmlr.next();

            if (eventType == XMLStreamConstants.CHARACTERS) {
                xmlr.getText();
                continue;
            }

            final String name = xmlr.getName().toString();

            if (eventType == XMLStreamConstants.START_ELEMENT) {
                if (name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.ENTRY)) {
                    final TableResult res = parseAtomEntity(xmlr, clazzType, resolver, opContext);
                    if (corePayload != null) {
                        corePayload.tableResults.add(res);
                    }

                    if (resolver != null) {
                        resolvedPayload.results.add((R) res.getResult());
                    } else {
                        corePayload.results.add((T) res.getResult());
                    }
                }
            } else if (eventType == XMLStreamConstants.END_ELEMENT
                    && name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.FEED)) {
                break;
            }
        }

        xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.FEED);
        return commonPayload;
    }

    /**
     * Reserved for internal use. Parses the operation response as an entity. Reads entity data from the specified
     * <code>XMLStreamReader</code> using the specified class type and optionally projects the entity result with the
     * specified resolver into a {@link TableResult} object.
     * 
     * @param xmlr
     *            The <code>XMLStreamReader</code> to read the data to parse from.
     * @param httpStatusCode
     *            The HTTP status code returned with the operation response.
     * @param clazzType
     *            The class type <code>T</code> implementing {@link TableEntity} for the entity returned. Set to
     *            <code>null</code> to ignore the returned entity and copy only response properties into the
     *            {@link TableResult} object.
     * @param resolver
     *            An {@link EntityResolver} instance to project the entity into an instance of type <code>R</code>. Set
     *            to <code>null</code> to return the entitys as instance of the class type <code>T</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @return
     *         A {@link TableResult} object with the parsed operation response.
     * 
     * @throws XMLStreamException
     *             if an error occurs while accessing the stream.
     * @throws ParseException
     *             if an error occurs while parsing the stream.
     * @throws InstantiationException
     *             if an error occurs while constructing the result.
     * @throws IllegalAccessException
     *             if an error occurs in reflection while parsing the result.
     * @throws StorageException
     *             if a storage service error occurs.
     */
    private static <T extends TableEntity, R> TableResult parseSingleOpAtomResponse(final InputStream inStream,
            final int httpStatusCode, final Class<T> clazzType, final EntityResolver<R> resolver,
            final OperationContext opContext) throws XMLStreamException, ParseException, InstantiationException,
            IllegalAccessException, StorageException {
        XMLStreamReader xmlr = Utility.createXMLStreamReaderFromStream(inStream);

        try {
            xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null);
            xmlr.next();

            final TableResult res = parseAtomEntity(xmlr, clazzType, resolver, opContext);
            res.setHttpStatusCode(httpStatusCode);
            return res;
        } finally {
            xmlr.close();
        }
    }

    /**
     * Reserved for internal use. Reads the properties of an entity from the stream into a map of property names to
     * typed values. Reads the entity data as an AtomPub Entry Resource from the specified {@link XMLStreamReader} into
     * a map of <code>String</code> property names to {@link EntityProperty} data typed values.
     * 
     * @param xmlr
     *            The <code>XMLStreamReader</code> to read the data from.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * 
     * @return
     *         A <code>java.util.HashMap</code> containing a map of <code>String</code> property names to
     *         {@link EntityProperty} data typed values found in the entity data.
     * @throws XMLStreamException
     *             if an error occurs accessing the stream.
     * @throws ParseException
     *             if an error occurs converting the input to a particular data type.
     */
    private static HashMap<String, EntityProperty> readAtomProperties(final XMLStreamReader xmlr,
            final OperationContext opContext) throws XMLStreamException, ParseException {
        int eventType = xmlr.getEventType();
        xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.PROPERTIES);
        final HashMap<String, EntityProperty> properties = new HashMap<String, EntityProperty>();

        while (xmlr.hasNext()) {
            eventType = xmlr.next();
            if (eventType == XMLStreamConstants.CHARACTERS) {
                xmlr.getText();
                continue;
            }

            if (eventType == XMLStreamConstants.START_ELEMENT
                    && xmlr.getNamespaceURI().equals(ODataConstants.DATA_SERVICES_NS)) {
                final String key = xmlr.getLocalName();
                String val = Constants.EMPTY_STRING;
                String edmType = null;

                if (xmlr.getAttributeCount() > 0) {
                    edmType = xmlr.getAttributeValue(ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE);
                }

                // move to chars
                eventType = xmlr.next();

                if (eventType == XMLStreamConstants.CHARACTERS) {
                    val = xmlr.getText();

                    // end element
                    eventType = xmlr.next();
                }

                xmlr.require(XMLStreamConstants.END_ELEMENT, null, key);

                final EntityProperty newProp = new EntityProperty(val, EdmType.parse(edmType));
                properties.put(key, newProp);
            } else if (eventType == XMLStreamConstants.END_ELEMENT && xmlr.getName().toString()
                    .equals(ODataConstants.BRACKETED_DATA_SERVICES_METADATA_NS + ODataConstants.PROPERTIES)) {
                // End read properties
                break;
            }
        }

        xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.PROPERTIES);
        return properties;
    }

    /**
     * Reserved for internal use. Writes an entity to the stream as an AtomPub Entry Resource, leaving the stream open
     * for additional writing.
     * 
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param xmlw
     *            The <code>XMLStreamWriter</code> to write the entity to.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * 
     * @throws XMLStreamException
     *             if an error occurs accessing the stream.
     * @throws StorageException
     *             if a Storage service error occurs.
     */
    private static void writeAtomEntity(final TableEntity entity, final boolean isTableEntry,
            final XMLStreamWriter xmlw, final OperationContext opContext)
            throws XMLStreamException, StorageException {
        HashMap<String, EntityProperty> properties = entity.writeEntity(opContext);
        if (properties == null) {
            properties = new HashMap<String, EntityProperty>();
        }

        if (!isTableEntry) {
            Utility.assertNotNull(TableConstants.PARTITION_KEY, entity.getPartitionKey());
            Utility.assertNotNull(TableConstants.ROW_KEY, entity.getRowKey());
            Utility.assertNotNull(TableConstants.TIMESTAMP, entity.getTimestamp());
        }

        // Begin entry
        xmlw.writeStartElement("entry");
        xmlw.writeNamespace("d", ODataConstants.DATA_SERVICES_NS);
        xmlw.writeNamespace("m", ODataConstants.DATA_SERVICES_METADATA_NS);

        // default namespace
        xmlw.writeNamespace(null, ODataConstants.ATOM_NS);

        // Content
        xmlw.writeStartElement(ODataConstants.CONTENT);
        xmlw.writeAttribute(ODataConstants.TYPE, ODataConstants.ODATA_CONTENT_TYPE);

        // m:properties
        xmlw.writeStartElement("m", ODataConstants.PROPERTIES, ODataConstants.DATA_SERVICES_METADATA_NS);

        if (!isTableEntry) {
            // d:PartitionKey
            xmlw.writeStartElement("d", TableConstants.PARTITION_KEY, ODataConstants.DATA_SERVICES_NS);
            xmlw.writeAttribute("xml", "xml", "space", "preserve");
            xmlw.writeCharacters(entity.getPartitionKey());
            xmlw.writeEndElement();

            // d:RowKey
            xmlw.writeStartElement("d", TableConstants.ROW_KEY, ODataConstants.DATA_SERVICES_NS);
            xmlw.writeAttribute("xml", "xml", "space", "preserve");
            xmlw.writeCharacters(entity.getRowKey());
            xmlw.writeEndElement();

            // d:Timestamp
            if (entity.getTimestamp() == null) {
                entity.setTimestamp(new Date());
            }

            xmlw.writeStartElement("d", TableConstants.TIMESTAMP, ODataConstants.DATA_SERVICES_NS);
            xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE,
                    EdmType.DATE_TIME.toString());
            xmlw.writeCharacters(Utility.getTimeByZoneAndFormat(entity.getTimestamp(), Utility.UTC_ZONE,
                    Utility.ISO8061_LONG_PATTERN));
            xmlw.writeEndElement();
        }

        for (final Entry<String, EntityProperty> ent : properties.entrySet()) {
            if (ent.getKey().equals(TableConstants.PARTITION_KEY) || ent.getKey().equals(TableConstants.ROW_KEY)
                    || ent.getKey().equals(TableConstants.TIMESTAMP) || ent.getKey().equals("Etag")) {
                continue;
            }

            EntityProperty currProp = ent.getValue();

            // d:PropName
            xmlw.writeStartElement("d", ent.getKey(), ODataConstants.DATA_SERVICES_NS);

            if (currProp.getEdmType() == EdmType.STRING) {
                xmlw.writeAttribute("xml", "xml", "space", "preserve");
            } else if (currProp.getEdmType().toString().length() != 0) {
                String edmTypeString = currProp.getEdmType().toString();
                if (edmTypeString.length() != 0) {
                    xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE,
                            edmTypeString);
                }
            }

            if (currProp.getIsNull()) {
                xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.NULL,
                        Constants.TRUE);
            }

            // Write Value
            xmlw.writeCharacters(currProp.getValueAsString());
            // End d:PropName
            xmlw.writeEndElement();
        }

        // End m:properties
        xmlw.writeEndElement();

        // End content
        xmlw.writeEndElement();

        // End entry
        xmlw.writeEndElement();
    }

    /**
     * Reserved for internal use. Writes a single entity to the specified <code>OutputStream</code> as a complete XML
     * document.
     * 
     * @param outStream
     *            The <code>OutputStream</code> to write the entity to.
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @throws XMLStreamException
     *             if an error occurs creating or accessing the stream.
     * @throws StorageException
     *             if a Storage service error occurs.
     */
    private static void writeSingleAtomEntity(final OutputStream outStream, final TableEntity entity,
            final boolean isTableEntry, final OperationContext opContext)
            throws XMLStreamException, StorageException {
        final XMLOutputFactory xmlOutFactoryInst = XMLOutputFactory.newInstance();
        XMLStreamWriter xmlw = xmlOutFactoryInst.createXMLStreamWriter(outStream, Constants.UTF8_CHARSET);

        // default is UTF8
        xmlw.writeStartDocument(Constants.UTF8_CHARSET, "1.0");

        writeAtomEntity(entity, isTableEntry, xmlw, opContext);

        // end doc
        xmlw.writeEndDocument();
        xmlw.flush();
    }

    /**
     * Reserved for internal use. Writes a single entity to the specified <code>OutputStream</code> as a complete XML
     * document.
     * 
     * @param entity
     *            The instance implementing {@link TableEntity} to write to the output stream.
     * @param isTableEntry
     *            A flag indicating the entity is a reference to a table at the top level of the storage service when
     *            <code>true<code> and a reference to an entity within a table when <code>false</code>.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @param outStream
     *            The <code>OutputStream</code> to write the entity to.
     * 
     * @throws XMLStreamException
     *             if an error occurs creating or accessing the stream.
     * @throws StorageException
     *             if a Storage service error occurs.
     */
    private static void writeSingleAtomEntity(final StringWriter strWriter, final TableEntity entity,
            final boolean isTableEntry, final OperationContext opContext)
            throws XMLStreamException, StorageException {
        final XMLOutputFactory xmlOutFactoryInst = XMLOutputFactory.newInstance();
        XMLStreamWriter xmlw = xmlOutFactoryInst.createXMLStreamWriter(strWriter);

        // default is UTF8
        xmlw.writeStartDocument(Constants.UTF8_CHARSET, "1.0");

        writeAtomEntity(entity, isTableEntry, xmlw, opContext);

        // end doc
        xmlw.writeEndDocument();
        xmlw.flush();
    }

    private static String getETagFromTimestamp(String timestampString) throws UnsupportedEncodingException {
        timestampString = URLEncoder.encode(timestampString, Constants.UTF8_CHARSET);
        return "W/\"datetime'" + timestampString + "'\"";
    }

    private static EdmType evaluateEdmType(JsonToken token, String value) throws JsonParseException, IOException {
        EdmType edmType = null;

        if (token == JsonToken.VALUE_NULL) {
            edmType = EdmType.NULL;
        } else if (token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_TRUE) {
            edmType = EdmType.BOOLEAN;
        } else if (token == JsonToken.VALUE_NUMBER_FLOAT) {
            edmType = EdmType.DOUBLE;
        } else if (token == JsonToken.VALUE_NUMBER_INT) {
            edmType = EdmType.INT32;
        } else {
            edmType = EdmType.STRING;
        }

        return edmType;
    }

    private static void writeJsonProperty(JsonGenerator generator, Entry<String, EntityProperty> prop)
            throws JsonGenerationException, IOException {
        EdmType edmType = prop.getValue().getEdmType();
        if (prop.getValue().getIsNull()) {
            generator.writeNullField(prop.getKey());
        } else if (edmType == EdmType.BOOLEAN) {
            generator.writeBooleanField(prop.getKey(), prop.getValue().getValueAsBoolean());
        } else if (edmType == EdmType.DOUBLE) {
            generator.writeNumberField(prop.getKey(), prop.getValue().getValueAsDouble());
        } else if (edmType == EdmType.INT32) {
            generator.writeNumberField(prop.getKey(), prop.getValue().getValueAsInteger());
        } else {
            generator.writeStringField(prop.getKey(), prop.getValue().getValueAsString());
        }
    }

    private static JsonParser createJsonParserFromStream(final InputStream streamRef)
            throws JsonParseException, IOException {
        JsonParser parser = jsonFactory.createParser(streamRef);

        // allows handling of infinity, -infinity, and NaN for Doubles
        return parser.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS);
    }
}