content: package edu.berkeley.cs.succinct.object.deserializer;
  
  import com.fasterxml.jackson.core.JsonProcessingException;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import edu.berkeley.cs.succinct.DataType;
  import edu.berkeley.cs.succinct.DeserializationException;
  import edu.berkeley.cs.succinct.PrimitiveDeserializer;
  import edu.berkeley.cs.succinct.block.json.FieldMapping;
  
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Map;
  
  public class JsonDeserializer implements ObjectDeserializer<String> {
  
    FieldMapping fieldMapping;
    ObjectMapper mapper;
  
    public JsonDeserializer(FieldMapping fieldMapping) {
      this.fieldMapping = fieldMapping;
      this.mapper = new ObjectMapper();
    }
  
    @Override public String deserialize(byte[] data) throws DeserializationException {
      return mapToJsonString(deserializeToMap(data));
    }
  
    public String deserialize(long id, byte[] data) throws DeserializationException {
      Map<String, Object> jsonMap = deserializeToMap(data);
      jsonMap.put("id", id);
      return mapToJsonString(jsonMap);
    }
  
    private String mapToJsonString(Map<String, Object> jsonMap) throws DeserializationException {
      String jsonString;
      try {
        jsonString = mapper.writeValueAsString(jsonMap);
      } catch (JsonProcessingException e) {
        throw new DeserializationException(e.getMessage());
      }
  
      return jsonString;
    }
  
    private Map<String, Object> deserializeToMap(byte[] data) throws DeserializationException {
      Map<String, Object> jsonMap = new HashMap<String, Object>();
  
      int curOffset = 0;
      while (curOffset < data.length) {
        byte curDelimiter = data[curOffset++];
        String fieldKey = fieldMapping.getField(curDelimiter);
        DataType fieldType = fieldMapping.getDataType(fieldKey);
        byte[] fieldValueBytes = extractField(data, curOffset, curDelimiter);
        Object fieldValue = PrimitiveDeserializer.deserializePrimitive(fieldValueBytes, fieldType);
        add(jsonMap, fieldKey, fieldValue);
        curOffset += (fieldValueBytes.length + 1); // Skip the field data, and the end delimiter
      }
  
      return jsonMap;
    }
  
    private void add(Map<String, Object> map, String key, Object value) {
      if (key.contains(".")) {
        String[] keySplits = key.split("\\.", 2);
        if (map.containsKey(keySplits[0])) {
          @SuppressWarnings("unchecked") Map<String, Object> valObj =
            (Map<String, Object>) map.get(keySplits[0]);
          add(valObj, keySplits[1], value);
        } else {
          Map<String, Object> valObj = new HashMap<String, Object>();
          add(valObj, keySplits[1], value);
          map.put(keySplits[0], valObj);
        }
      } else {
        map.put(key, value);
      }
    }
  
    private byte[] extractField(byte[] data, int startOffset, byte delimiter) {
      int i = startOffset;
      while (data[i] != delimiter) {
        i++;
      }
      return Arrays.copyOfRange(data, startOffset, i);
    }
  
  }
  