diff --git a/core/src/main/java/com/orientechnologies/orient/core/fetch/json/OJSONFetchContext.java b/core/src/main/java/com/orientechnologies/orient/core/fetch/json/OJSONFetchContext.java index 1ac4dbd5c74..bef1b568073 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/fetch/json/OJSONFetchContext.java +++ b/core/src/main/java/com/orientechnologies/orient/core/fetch/json/OJSONFetchContext.java @@ -29,7 +29,7 @@ import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.record.impl.ODocumentHelper; import com.orientechnologies.orient.core.serialization.serializer.OJSONWriter; -import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerJSON; +import com.orientechnologies.orient.core.serialization.serializer.record.string.OFieldTypesString; import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerJSON.FormatSettings; import java.io.IOException; import java.math.BigDecimal; @@ -61,7 +61,7 @@ public void onAfterFetch(final ODocument rootRecord) { jsonWriter.writeAttribute( settings.indentLevel > -1 ? settings.indentLevel : 1, true, - ORecordSerializerJSON.ATTRIBUTE_FIELD_TYPES, + OFieldTypesString.ATTRIBUTE_FIELD_TYPES, sb.toString()); } catch (final IOException e) { throw OException.wrapException(new OFetchException("Error writing field types"), e); diff --git a/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocumentHelper.java b/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocumentHelper.java index cad90ea46d3..3ad7c900aa0 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocumentHelper.java +++ b/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocumentHelper.java @@ -87,6 +87,7 @@ public class ODocumentHelper { public static final String ATTRIBUTE_TYPE = "@type"; public static final String ATTRIBUTE_SIZE = "@size"; public static final String ATTRIBUTE_FIELDS = "@fields"; + public static final String ATTRIBUTE_FIELS_TYPES = "@fieldtypes"; public static final String ATTRIBUTE_RAW = "@raw"; public static interface ODbRelatedCall { @@ -109,6 +110,7 @@ public static Set getReservedAttributes() { retSet.add(ATTRIBUTE_SIZE); retSet.add(ATTRIBUTE_FIELDS); retSet.add(ATTRIBUTE_RAW); + retSet.add(ATTRIBUTE_FIELS_TYPES); return retSet; } diff --git a/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/OFieldTypesString.java b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/OFieldTypesString.java new file mode 100644 index 00000000000..1d85b7f8b96 --- /dev/null +++ b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/OFieldTypesString.java @@ -0,0 +1,84 @@ +package com.orientechnologies.orient.core.serialization.serializer.record.string; + +import com.orientechnologies.orient.core.metadata.schema.OType; +import java.util.HashMap; +import java.util.Map; + +public class OFieldTypesString { + + public static final String ATTRIBUTE_FIELD_TYPES = "@fieldTypes"; + + /** + * Parses the field type char returning the closer type. Default is STRING. b=binary if + * iValue.length() >= 4 b=byte if iValue.length() <= 3 s=short, l=long f=float d=double a=date + * t=datetime + * + * @param iValue Value to parse + * @param iCharType Char value indicating the type + * @return The closest type recognized + */ + public static OType getType(final String iValue, final char iCharType) { + if (iCharType == 'f') return OType.FLOAT; + else if (iCharType == 'c') return OType.DECIMAL; + else if (iCharType == 'l') return OType.LONG; + else if (iCharType == 'd') return OType.DOUBLE; + else if (iCharType == 'b') { + if (iValue.length() >= 1 && iValue.length() <= 3) return OType.BYTE; + else return OType.BINARY; + } else if (iCharType == 'a') return OType.DATE; + else if (iCharType == 't') return OType.DATETIME; + else if (iCharType == 's') return OType.SHORT; + else if (iCharType == 'e') return OType.EMBEDDEDSET; + else if (iCharType == 'g') return OType.LINKBAG; + else if (iCharType == 'z') return OType.LINKLIST; + else if (iCharType == 'm') return OType.LINKMAP; + else if (iCharType == 'x') return OType.LINK; + else if (iCharType == 'n') return OType.LINKSET; + else if (iCharType == 'u') return OType.CUSTOM; + + return OType.STRING; + } + + public static OType getOTypeFromChar(final char iCharType) { + if (iCharType == 'f') return OType.FLOAT; + else if (iCharType == 'c') return OType.DECIMAL; + else if (iCharType == 'l') return OType.LONG; + else if (iCharType == 'd') return OType.DOUBLE; + else if (iCharType == 'b') return OType.BINARY; + else if (iCharType == 'a') return OType.DATE; + else if (iCharType == 't') return OType.DATETIME; + else if (iCharType == 's') return OType.SHORT; + else if (iCharType == 'e') return OType.EMBEDDEDSET; + else if (iCharType == 'g') return OType.LINKBAG; + else if (iCharType == 'z') return OType.LINKLIST; + else if (iCharType == 'm') return OType.LINKMAP; + else if (iCharType == 'x') return OType.LINK; + else if (iCharType == 'n') return OType.LINKSET; + else if (iCharType == 'u') return OType.CUSTOM; + + return OType.STRING; + } + + public static Map loadFieldTypesV0( + Map fieldTypes, final String fieldValueAsString) { + // LOAD THE FIELD TYPE MAP + final String[] fieldTypesParts = fieldValueAsString.split(","); + if (fieldTypesParts.length > 0) { + if (fieldTypes == null) { + fieldTypes = new HashMap<>(); + } + String[] part; + for (String f : fieldTypesParts) { + part = f.split("="); + if (part.length == 2) fieldTypes.put(part[0], part[1].charAt(0)); + } + } + return fieldTypes; + } + + public static Map loadFieldTypes(final String fieldValueAsString) { + Map fieldTypes = new HashMap<>(); + loadFieldTypesV0(fieldTypes, fieldValueAsString); + return fieldTypes; + } +} diff --git a/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerJSON.java b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerJSON.java index 4a292afaa74..024e73cb1a6 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerJSON.java +++ b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerJSON.java @@ -28,7 +28,12 @@ import com.orientechnologies.orient.core.Orient; import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; import com.orientechnologies.orient.core.db.document.ODatabaseDocument; -import com.orientechnologies.orient.core.db.record.*; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.db.record.ORecordLazyList; +import com.orientechnologies.orient.core.db.record.ORecordLazyMultiValue; +import com.orientechnologies.orient.core.db.record.ORecordLazySet; +import com.orientechnologies.orient.core.db.record.OTrackedList; +import com.orientechnologies.orient.core.db.record.OTrackedSet; import com.orientechnologies.orient.core.db.record.ridbag.ORidBag; import com.orientechnologies.orient.core.exception.OSerializationException; import com.orientechnologies.orient.core.fetch.OFetchHelper; @@ -43,19 +48,32 @@ import com.orientechnologies.orient.core.record.ORecord; import com.orientechnologies.orient.core.record.ORecordInternal; import com.orientechnologies.orient.core.record.ORecordStringable; -import com.orientechnologies.orient.core.record.impl.*; +import com.orientechnologies.orient.core.record.impl.OBlob; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.record.impl.ODocumentEmbedded; +import com.orientechnologies.orient.core.record.impl.ODocumentHelper; +import com.orientechnologies.orient.core.record.impl.ODocumentInternal; import com.orientechnologies.orient.core.serialization.serializer.OJSONWriter; import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper; import com.orientechnologies.orient.core.util.ODateHelper; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.StringWriter; import java.text.ParseException; -import java.util.*; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; @SuppressWarnings("serial") public class ORecordSerializerJSON extends ORecordSerializerStringAbstract { public static final String NAME = "json"; public static final ORecordSerializerJSON INSTANCE = new ORecordSerializerJSON(); - public static final String ATTRIBUTE_FIELD_TYPES = "@fieldTypes"; public static final char[] PARAMETER_SEPARATOR = new char[] {':', ','}; public static final int INITIAL_SIZE = 5000; private static final Long MAX_INT = (long) Integer.MAX_VALUE; @@ -219,8 +237,9 @@ public ORecord fromStringV0( final String fieldValue = fields.get(i + 1); final String fieldValueAsString = OIOUtils.getStringContent(fieldValue); - if (fieldName.equals(ATTRIBUTE_FIELD_TYPES) && record instanceof ODocument) { - fieldTypes = loadFieldTypesV0(fieldTypes, fieldValueAsString); + if (fieldName.equals(OFieldTypesString.ATTRIBUTE_FIELD_TYPES) + && record instanceof ODocument) { + fieldTypes = OFieldTypesString.loadFieldTypesV0(fieldTypes, fieldValueAsString); } else if (fieldName.equals(ODocumentHelper.ATTRIBUTE_TYPE)) { if (record == null || ORecordInternal.getRecordType(record) != fieldValueAsString.charAt(0)) { @@ -298,7 +317,8 @@ private void processRecordsV0( return; } else if (fieldName.equals(ODocumentHelper.ATTRIBUTE_TYPE)) { return; - } else if (fieldName.equals(ATTRIBUTE_FIELD_TYPES) && record instanceof ODocument) { + } else if (fieldName.equals(OFieldTypesString.ATTRIBUTE_FIELD_TYPES) + && record instanceof ODocument) { return; } else if (fieldName.equals("value") && !(record instanceof ODocument)) { // RECORD VALUE(S) @@ -367,7 +387,7 @@ else if (record instanceof OBlob) { } if (type == null && fieldTypes != null && fieldTypes.containsKey(fieldName)) - type = ORecordSerializerStringAbstract.getType(fieldValue, fieldTypes.get(fieldName)); + type = OFieldTypesString.getType(fieldValue, fieldTypes.get(fieldName)); if (v instanceof OTrackedSet) { if (OMultiValue.getFirstValue(v) instanceof OIdentifiable) type = OType.LINKSET; @@ -487,21 +507,6 @@ private OType determineType(ODocument doc, String fieldName) { return type; } - private Map loadFieldTypesV0( - Map fieldTypes, final String fieldValueAsString) { - // LOAD THE FIELD TYPE MAP - final String[] fieldTypesParts = fieldValueAsString.split(","); - if (fieldTypesParts.length > 0) { - fieldTypes = new HashMap<>(); - String[] part; - for (String f : fieldTypesParts) { - part = f.split("="); - if (part.length == 2) fieldTypes.put(part[0], part[1].charAt(0)); - } - } - return fieldTypes; - } - @SuppressWarnings("unchecked") private Object getValueV0( final ODocument iRecord, @@ -525,7 +530,7 @@ private Object getValueV0( } if (iType == null && iFieldTypes != null && iFieldTypes.containsKey(iFieldName)) - iType = ORecordSerializerStringAbstract.getType(iFieldValue, iFieldTypes.get(iFieldName)); + iType = OFieldTypesString.getType(iFieldValue, iFieldTypes.get(iFieldName)); if (iFieldValue.startsWith("{") && iFieldValue.endsWith("}")) { // Json object @@ -577,7 +582,7 @@ else if (iFieldValue.matches(".*[\\.Ee].*")) { if (iFieldTypes != null) { Character c = iFieldTypes.get(iFieldName); - if (c != null) iType = ORecordSerializerStringAbstract.getType(iFieldValueAsString, c); + if (c != null) iType = OFieldTypesString.getType(iFieldValueAsString, c); } if (iType == null) iType = OType.STRING; @@ -670,13 +675,6 @@ private boolean canBeTrunkedToInt(Long v) { return (v > 0) ? v.compareTo(MAX_INT) <= 0 : v.compareTo(MIN_INT) >= 0; } - private boolean canBeTrunkedToFloat(Double v) { - // TODO not really correct check. Small numbers with high precision will be trunked while they - // shouldn't be - - return (v > 0) ? v.compareTo(MAX_FLOAT) <= 0 : v.compareTo(MIN_FLOAT) >= 0; - } - /** OBJECT OR MAP. CHECK THE TYPE ATTRIBUTE TO KNOW IT. */ private Object getValueAsObjectOrMapV0( ODocument iRecord, diff --git a/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerStringAbstract.java b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerStringAbstract.java index 20d5edae2a7..d910ad2aad2 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerStringAbstract.java +++ b/core/src/main/java/com/orientechnologies/orient/core/serialization/serializer/record/string/ORecordSerializerStringAbstract.java @@ -387,37 +387,6 @@ && new Double(new Double(dou).floatValue()).doubleValue() == dou) { return OType.DOUBLE; } - /** - * Parses the field type char returning the closer type. Default is STRING. b=binary if - * iValue.length() >= 4 b=byte if iValue.length() <= 3 s=short, l=long f=float d=double a=date - * t=datetime - * - * @param iValue Value to parse - * @param iCharType Char value indicating the type - * @return The closest type recognized - */ - public static OType getType(final String iValue, final char iCharType) { - if (iCharType == 'f') return OType.FLOAT; - else if (iCharType == 'c') return OType.DECIMAL; - else if (iCharType == 'l') return OType.LONG; - else if (iCharType == 'd') return OType.DOUBLE; - else if (iCharType == 'b') { - if (iValue.length() >= 1 && iValue.length() <= 3) return OType.BYTE; - else return OType.BINARY; - } else if (iCharType == 'a') return OType.DATE; - else if (iCharType == 't') return OType.DATETIME; - else if (iCharType == 's') return OType.SHORT; - else if (iCharType == 'e') return OType.EMBEDDEDSET; - else if (iCharType == 'g') return OType.LINKBAG; - else if (iCharType == 'z') return OType.LINKLIST; - else if (iCharType == 'm') return OType.LINKMAP; - else if (iCharType == 'x') return OType.LINK; - else if (iCharType == 'n') return OType.LINKSET; - else if (iCharType == 'u') return OType.CUSTOM; - - return OType.STRING; - } - /** * Parses a string returning the value with the closer type. Numbers by default are INTEGER if * haven't decimal separator, otherwise FLOAT. To treat all the number types numbers are postponed diff --git a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OJson.java b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OJson.java index 46c690152e5..3fd2d787667 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OJson.java +++ b/core/src/main/java/com/orientechnologies/orient/core/sql/parser/OJson.java @@ -4,8 +4,10 @@ import com.orientechnologies.orient.core.command.OCommandContext; import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.record.impl.ODocumentHelper; +import com.orientechnologies.orient.core.serialization.serializer.record.string.OFieldTypesString; import com.orientechnologies.orient.core.sql.executor.OResult; import com.orientechnologies.orient.core.sql.executor.OResultInternal; import java.util.ArrayList; @@ -83,14 +85,34 @@ public ODocument toDocument(OIdentifiable source, OCommandContext ctx) { private ODocument toDocument(OResult source, OCommandContext ctx, String className) { ODocument retDoc = new ODocument(className); + Map types = null; for (OJsonItem item : items) { String name = item.getLeftValue(); if (name == null || ODocumentHelper.getReservedAttributes().contains(name.toLowerCase(Locale.ENGLISH))) { + if (name.equals(OFieldTypesString.ATTRIBUTE_FIELD_TYPES)) { + Object value = item.right.execute(source, ctx); + types = OFieldTypesString.loadFieldTypes(value.toString()); + for (Map.Entry entry : types.entrySet()) { + OType t = OFieldTypesString.getOTypeFromChar(entry.getValue()); + retDoc.setFieldType(entry.getKey(), t); + } + } continue; } Object value = item.right.execute(source, ctx); - retDoc.field(name, value); + Character charType; + if (types != null) { + charType = types.get(name); + } else { + charType = null; + } + if (charType != null) { + OType t = OFieldTypesString.getOTypeFromChar(charType); + retDoc.setProperty(name, value, t); + } else { + retDoc.setProperty(name, value); + } } return retDoc; }