OK, поэтому, вопрос гласит правильно: это не представляется возможным использовать карты в формате JSON, в котором ключи не являются строками. Как следствие, чтобы эмулировать Java-карту в javascript, нужно пройти более длинный путь, который обычно включает преобразование карты в ... что-то еще.
То, что я выбрал был вполне обычный массив массивов:
карта, такие как
{
a:b,
c:d,
}
затем будет переведен в массив
[
[a,b],
[c,d],
]
Каковы Детальные шаги, необходимые для получения этого результата
Настройка пользовательской (де) сериализации
Это достигается путем установки сериализации завода в объекте картограф, а Jackson doc clearly explains:
/**
* Associates all maps with our custom serialization mechanism, which will transform them into arrays of arrays
* @see MapAsArraySerializer
* @return
*/
@Produces
public SerializerFactory createSerializerFactory() {
CustomSerializerFactory customized = new CustomSerializerFactory();
customized.addGenericMapping(Map.class, new MapAsArraySerializer());
return customized;
}
public @Produces ObjectMapper createMapper() {
ObjectMapper jsonMapper = new ObjectMapper();
// ....
// now configure serializer
jsonMapper.setSerializerFactory(createSerializerFactory());
// ....
return jsonMapper;
}
Процесс кажется довольно простой, в основном потому, что сериализации обеспечивают вполне правильные черты полиморфизма в сериализации, которые не являются, что хороши для десериализации. Действительно, как doc also states, десериализация требует добавлений явного отображения классов, которые не используются в любом объектно-ориентированном способе (Наследование является не поддерживается там)
/**
* Defines a deserializer for each and any used map class, as there is no inheritence support ind eserialization
* @return
*/
@Produces
public DeserializerProvider createDeserializationProvider() {
// Yeah it's not even a standard Jackson class, it'll be explained why later
CustomDeserializerFactory factory = new MapAsArrayDeserializerFactory();
List<Class<? extends Map>> classesToHandle = new LinkedList<>();
classesToHandle.add(HashMap.class);
classesToHandle.add(LinkedHashMap.class);
classesToHandle.add(TreeMap.class);
for(Class<? extends Map> c : classesToHandle) {
addClassMappingFor(c, c, factory);
}
// and don't forget interfaces !
addClassMappingFor(Map.class, HashMap.class, factory);
addClassMappingFor(SortedMap.class, TreeMap.class, factory);
return new StdDeserializerProvider(factory);
}
private void addClassMappingFor(final Class<? extends Map> detected, final Class<? extends Map> created, CustomDeserializerFactory factory) {
factory.addSpecificMapping(detected, new MapAsArrayDeserializer() {
@Override
protected Map createNewMap() throws Exception {
return created.newInstance();
}
});
}
// It's the same createMapper() method that was described upper
public @Produces ObjectMapper createMapper() {
ObjectMapper jsonMapper = new ObjectMapper();
// ....
// and deserializer
jsonMapper.setDeserializerProvider(createDeserializationProvider());
return jsonMapper;
}
Теперь мы правильно определили, как настроить (де) сериализации , или у нас есть? Фактически, нет: MapAsArrayDeserializerFactory
заслуживает собственного объяснения.
После некоторой отладки я обнаружил, что DeserializerProvider
делегирует DeserializerFactory
, если для класса не существует десериализатора, что очень круто. Но DeserializerFactory
создает десериализатор в соответствии с «видом» obejct: если это коллекция, то будет использоваться CollectionDeserializer (чтобы прочитать массив в коллекции). Если это карта, то будет использоваться MapDeserializer.
К сожалению, это разрешение использует класс java, указанный в потоке JSON (особенно при использовании polymorphic deserialization, что было моим делом). Как следствие, настройка пользовательских десериализации не имеет никакого эффекта, если CustomDeserializerFactory
не настроен ... как то:
public class MapAsArrayDeserializerFactory extends CustomDeserializerFactory {
@Override
public JsonDeserializer<?> createMapDeserializer(DeserializationConfig config, MapType type, DeserializerProvider p) throws JsonMappingException {
return createBeanDeserializer(config, type, p);
}
}
Да, я десериализации все карты как бобы. Но теперь все мои десериализаторы правильно названы.
Сериализации
Теперь сериализация довольно простая задача:
public class MapAsArraySerializer extends JsonSerializer<Map> {
@SuppressWarnings("unchecked")
private Set asListOfLists(Map<?, ?> value) {
Set returned = new HashSet<>();
for(Map.Entry e : value.entrySet()) {
returned.add(Arrays.asList(e.getKey(), e.getValue()));
}
return returned;
}
@Override
public void serialize(Map value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Collection entries = asListOfLists(value);
jgen.writeObjectField("entries", entries);
}
@Override
public void serializeWithType(Map value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException,
JsonProcessingException {
Collection entries = asListOfLists(value);
typeSer.writeTypePrefixForObject(value, jgen);
jgen.writeObjectField("entries", entries);
typeSer.writeTypeSuffixForObject(value, jgen);
}
}
Десериализация
И десериализация не сложнее:
public abstract class MapAsArrayDeserializer<Type extends Map> extends JsonDeserializer<Type> {
protected Type newMap(Collection c, Type returned) {
for(Object o : c) {
if (o instanceof List) {
List l = (List) o;
if(l.size()==2) {
Iterator i = l.iterator();
returned.put(i.next(), i.next());
}
}
}
return returned;
}
protected abstract Type createNewMap() throws Exception;
@Override
public Type deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
if(jp.getCurrentToken().equals(JsonToken.START_OBJECT)) {
JsonToken nameToken = jp.nextToken();
String name = jp.getCurrentName();
if(name.equals("entries")) {
jp.nextToken();
Collection entries = jp.readValueAs(Collection.class);
JsonToken endMap = jp.nextToken();
try {
return newMap(entries, createNewMap());
} catch(Exception e) {
throw new IOException("unable to create receiver map", e);
}
} else {
throw new IOException("expected \"entries\", but field name was \""+name+"\"");
}
} else {
throw new IOException("not startying an object ? Not possible");
}
}
@Override
public Type deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException,
JsonProcessingException {
Object value = typeDeserializer.deserializeTypedFromObject(jp, ctxt);
return (Type) value;
}
}
Ну, ожидал, что класс левый абстрактный t, каждый объявленный подтип создает правильную малую p instance.
А теперь
И теперь он работает плавно на Java стороны (вызвать Javascript должно иметь карту-эквивалентный объект для чтения этих ДАННЫХ.
Это не имеет значения, но вы действительно должны обновить более поздняя версия Джексона. Если вы хотите остаться с 1.x, 1.9 (.13) - последнее - многие ошибки были исправлены с 1.5. – StaxMan
@StaxMan хорошо, я думал об этом, но не имею но встретил любую реальную ошибку Джексона, но я могу передумать ... – Riduidel