github.com/altipla-consulting/ravendb-go-client@v0.1.3/entity_to_json.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  // TODO: cleanup, possibly rethink entityToJSON
    10  
    11  type entityToJSON struct {
    12  	session           *InMemoryDocumentSessionOperations
    13  	missingDictionary map[interface{}]map[string]interface{}
    14  	//private final Map<Object, Map<string, Object>> _missingDictionary = new TreeMap<>((o1, o2) -> o1 == o2 ? 0 : 1);
    15  }
    16  
    17  // All the listeners for this session
    18  func newEntityToJSON(session *InMemoryDocumentSessionOperations) *entityToJSON {
    19  	return &entityToJSON{
    20  		session: session,
    21  	}
    22  }
    23  
    24  func (e *entityToJSON) getMissingDictionary() map[interface{}]map[string]interface{} {
    25  	return e.missingDictionary
    26  }
    27  
    28  func convertEntityToJSON(entity interface{}, documentInfo *documentInfo) map[string]interface{} {
    29  	// maybe we don't need to do anything?
    30  	if v, ok := entity.(map[string]interface{}); ok {
    31  		return v
    32  	}
    33  	jsonNode := structToJSONMap(entity)
    34  
    35  	entityToJSONWriteMetadata(jsonNode, documentInfo)
    36  
    37  	tryRemoveIdentityProperty(jsonNode)
    38  
    39  	return jsonNode
    40  }
    41  
    42  // TODO: verify is correct, write a test
    43  func isTypeObjectNode(entityType reflect.Type) bool {
    44  	var v map[string]interface{}
    45  	typ := reflect.ValueOf(v).Type()
    46  	return typ.String() == entityType.String()
    47  }
    48  
    49  // assumes v is ptr-to-struct and result is ptr-to-ptr-to-struct
    50  func setInterfaceToValue(result interface{}, v interface{}) (err error) {
    51  
    52  	// this catches a panic that reflect.Value.Set() can produce
    53  	// and turns it into an error
    54  	// TODO: a cleaner way would be to check instead suppressing a panic by e.g.
    55  	// lifting implementation of func directlyAssignable(T, V *rtype) bool {
    56  	// from reflect package
    57  	defer func() {
    58  		if res := recover(); res != nil {
    59  			fmt.Printf("setInterfaceToValue: panic, res: %v %T\n", res, res)
    60  			if s, ok := res.(string); ok {
    61  				err = errors.New(s)
    62  			} else if panicErr, ok := res.(error); ok {
    63  				err = panicErr
    64  			} else {
    65  				err = fmt.Errorf("%v", res)
    66  			}
    67  		}
    68  	}()
    69  
    70  	out := reflect.ValueOf(result)
    71  	outt := out.Type()
    72  	if outt.Kind() == reflect.Ptr && out.IsNil() {
    73  		out.Set(reflect.New(outt.Elem()))
    74  	}
    75  	if outt.Kind() == reflect.Ptr {
    76  		out = out.Elem()
    77  		//outt = out.Type()
    78  		//outk = out.Kind()
    79  	}
    80  
    81  	vin := reflect.ValueOf(v)
    82  	if !out.CanSet() {
    83  		return fmt.Errorf("cannot set out %s\n", out.String())
    84  	}
    85  
    86  	out.Set(vin)
    87  	return
    88  }
    89  
    90  // makes a copy of a map and returns a pointer to it
    91  func mapDup(m map[string]interface{}) *map[string]interface{} {
    92  	res := map[string]interface{}{}
    93  	for k, v := range m {
    94  		res[k] = v
    95  	}
    96  	return &res
    97  }
    98  
    99  // ConvertToEntity2 converts document to a value result, matching type of result
   100  func (e *entityToJSON) convertToEntity2(result interface{}, id string, document map[string]interface{}) error {
   101  	if _, ok := result.(**map[string]interface{}); ok {
   102  		return setInterfaceToValue(result, mapDup(document))
   103  	}
   104  
   105  	if _, ok := result.(map[string]interface{}); ok {
   106  		// TODO: is this code path ever executed?
   107  		return setInterfaceToValue(result, document)
   108  	}
   109  	entityType := reflect.TypeOf(result)
   110  	entity, err := makeStructFromJSONMap(entityType, document)
   111  	if err != nil {
   112  		// fmt.Printf("makeStructFromJSONMap() failed with %s\n. Wanted type: %s, document: %v\n", err, entityType, document)
   113  		return err
   114  	}
   115  	trySetIDOnEntity(entity, id)
   116  	//fmt.Printf("result is: %T, entity is: %T\n", result, entity)
   117  	if entity == nil {
   118  		return newIllegalStateError("decoded entity is nil")
   119  	}
   120  	return setInterfaceToValue(result, entity)
   121  }
   122  
   123  // Converts a json object to an entity.
   124  // TODO: remove in favor of entityToJSONConvertToEntity
   125  func (e *entityToJSON) convertToEntity(entityType reflect.Type, id string, document map[string]interface{}) (interface{}, error) {
   126  	if isTypeObjectNode(entityType) {
   127  		return document, nil
   128  	}
   129  	entity, err := makeStructFromJSONMap(entityType, document)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	trySetIDOnEntity(entity, id)
   134  	return entity, nil
   135  }
   136  
   137  func entityToJSONConvertToEntity(entityType reflect.Type, id string, document map[string]interface{}) (interface{}, error) {
   138  	if isTypeObjectNode(entityType) {
   139  		return document, nil
   140  	}
   141  	entity, err := makeStructFromJSONMap(entityType, document)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	trySetIDOnEntity(entity, id)
   146  	return entity, nil
   147  }
   148  
   149  func entityToJSONWriteMetadata(jsonNode map[string]interface{}, documentInfo *documentInfo) {
   150  	if documentInfo == nil {
   151  		return
   152  	}
   153  
   154  	setMetadata := false
   155  	metadataNode := map[string]interface{}{}
   156  
   157  	metadata := documentInfo.metadata
   158  	metadataInstance := documentInfo.metadataInstance
   159  	if len(metadata) > 0 {
   160  		setMetadata = true
   161  		for property, v := range metadata {
   162  			v = deepCopy(v)
   163  			metadataNode[property] = v
   164  		}
   165  	} else if metadataInstance != nil {
   166  		setMetadata = true
   167  		for key, value := range metadataInstance.EntrySet() {
   168  			metadataNode[key] = value
   169  		}
   170  	}
   171  
   172  	collection := documentInfo.collection
   173  	if collection != "" {
   174  		setMetadata = true
   175  
   176  		metadataNode[MetadataCollection] = collection
   177  	}
   178  
   179  	if setMetadata {
   180  		jsonNode[MetadataKey] = metadataNode
   181  	}
   182  }
   183  
   184  /*
   185      //TBD public static object ConvertToEntity(Type entityType, string id, BlittableJsonReaderObject document, DocumentConventions conventions)
   186  
   187  }
   188  */
   189  
   190  func tryRemoveIdentityProperty(document map[string]interface{}) bool {
   191  	delete(document, IdentityProperty)
   192  	return true
   193  }
   194  
   195  /*
   196     public static Object convertToEntity(Class<?> entityClass, String id, ObjectNode document, DocumentConventions conventions) {
   197         try {
   198             Object defaultValue = InMemoryDocumentSessionOperations.getDefaultValue(entityClass);
   199  
   200             Object entity = defaultValue;
   201  
   202             String documentType = conventions.getJavaClass(id, document);
   203             if (documentType != null) {
   204                 Class<?> clazz = Class.forName(documentType);
   205                 if (clazz != null && entityClass.isAssignableFrom(clazz)) {
   206                     entity = conventions.getEntityMapper().treeToValue(document, clazz);
   207                 }
   208             }
   209  
   210             if (entity == null) {
   211                 entity = conventions.getEntityMapper().treeToValue(document, entityClass);
   212             }
   213  
   214             return entity;
   215         } catch (Exception e) {
   216             throw new IllegalStateException("Could not convert document " + id + " to entity of type " + entityClass);
   217         }
   218     }
   219  */