mongodb Morphia 2.3Map返回String而不是Locale作为Map键

e7arh2l6  于 2023-06-29  发布在  Go
关注(0)|答案(1)|浏览(150)

我们正在从Morphia 1.3.2升级到Morphia 2.3.2
在我们最近升级的微服务中,有一个包含字段messageTemplates的类:

private Map<Locale, MessageTemplate> messageTemplates;

    public Map<Locale, MessageTemplate> getMessageTemplates() {
        if(messageTemplates == null){
            this.messageTemplates = new HashMap<>();
        }
        return messageTemplates;
    }

    public void setMessageTemplates(Map<Locale, MessageTemplate> messageTemplates) {
        this.messageTemplates = messageTemplates;
    }

Mongo集合中该字段的json数据如下所示:

"_id" : "55c8a25e-137c-44f8-8e75-18617aafd374",
    "className" : "com.swipejobs.desktopnotifications.model.NotificationTypeDetails",
    "messageTemplates" : {
        "en" : {
            "subject" : "",
            "body" : "Your verification PIN is ${pincode}. Please do not reply to this message.",
            "emailSubject" : "",
            "emailBody" : ""
        }
    },

当从集合中检索文档时,Map中的键是String类的对象,而不是Locale类型的对象。因此,Map的键在保存到集合时被转换为String,但在检索文档时似乎不会Map回Locale。
我们的MongoClient设置如下:

import com.swipejobs.dataaccess.codecs.ZoneIdCodec;
import com.swipejobs.dataaccess.codecs.ZonedDateTimeCodec;
import dev.morphia.Datastore;
import dev.morphia.mapping.codec.LocaleCodec;

        CodecRegistry codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new ZonedDateTimeCodec(),
                                                                                                new ZoneIdCodec(),
                                                                                                new LocaleCodec()),
                                                                     MongoClientSettings.getDefaultCodecRegistry(),
                                                                     CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()));
ConnectionString connectionString = new ConnectionString(uri);
MongoClientSettings settings = MongoClientSettings.builder().codecRegistry(codecRegistry).applyConnectionString(connectionString).build();

MongoClient mongoClient = MongoClients.create(settings);

MapperOptions options = MapperOptions.builder().discriminator(DiscriminatorFunction.className()).discriminatorKey("className").collectionNaming(NamingStrategy.identity()).build();

mDataStore = Morphia.createDatastore(mMongoClient, mDbName, options);
mDataStore.ensureIndexes();
datastore.getMapper().map(NotificationTypeDetails.class);

有谁知道这个场景吗?
我们期望Map中的键是Locale类型而不是String类型。
我们添加了以下hack:

private Map<Locale, MessageTemplate> fixMap(Map<Locale, MessageTemplate> map){
        return map.entrySet().stream()
                             .map(this::fixEntry)
                             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    private Map.Entry<Locale, MessageTemplate> fixEntry(Map.Entry<Locale, MessageTemplate> entry) {
        Locale key = fixKey(entry.getKey());
        MessageTemplate value = entry.getValue();

        return Map.of(key, value).entrySet().stream().findFirst().get();
    }

    private Locale fixKey(Object key){
        if (key.getClass().equals(Locale.class))
            return (Locale) key;
        if (key.getClass().equals(String.class))
            return new Locale((String) key);
        return Locale.ENGLISH;
    }

这解决了我们的问题,但没有解释为什么会发生。正如前面所看到的,我们已经添加了LocaleCodec,但它似乎什么也没做。

nmpmafwu

nmpmafwu1#

我实际上是修复了一个错误有关的这只是昨晚,并做了一些挖掘。由于许多技术原因,Map键不受编解码器解/串行化的影响。这些键使用Conversions条目转换为String s/从String s转换。现在这并不完全是一个 * 公共 * API,但它不时被不同的用户使用。由于它不是一个公共API,它可能会在你的控制下发生变化,但它已经稳定了几年,所以我认为这种恐惧在这一点上主要是假设的。接下来,我将向您展示如何定义一个:
要注册一个转换,需要调用Conversions.regsiter(),它具有以下签名

public static <S, T> void register(Class<S> source, Class<T> target, Function<S, T> function)

下面是其中一个内置转换:

register(String.class, URI.class, str -> URI.create(str.replace("%46", ".")));

此转换将从目标类型为URI的数据库中获取String,并调用适当的代码进行转换。在本例中,它是URI.create(它对字符串中的转义字符进行了一些清理)。对于Locale,您将执行类似的操作。有两点需要注意:
1.此转换适用于 allString-> Locale转换,因此需要进行一般化。在本例中,Locale是一个非常普通的类型,因此对特殊和特定转换的需求几乎肯定为0。
1.在向数据库写入 to 时,如果没有找到转换,则所使用的Map编解码器将简单地toString()键,因为数据库中的文档键只能是字符串。这可能不会产生您想要的格式,或者可以简单地传递回Locale方法以解析出的格式。如果是这样的话,你需要在应用程序代码中的某个地方做`register(Locale.class,String.class,l -> { /* convert your Locale to a String here /* })。
这应该能帮你摆平如果由于某种原因这不起作用,请提交一个issue或在GitHub上打开一个discussion,我会尽力让你修复。

相关问题