Morphia 2.3映射返回字符串而不是地区作为映射键

huangapple go评论73阅读模式
英文:

Morphia 2.3 mapping returning String instead of Locale as Map Key

问题

我们正在从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类型的对象。因此,映射的键在保存到集合时会转换为String,但在检索文档时似乎没有映射回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类型。

我们添加了以下补丁:

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,但似乎没有起作用。

英文:

We are in the process of upgrading from Morphia 1.3.2 to Morphia 2.3.2

In the microservice we have most recently upgraded, there is a class containing a field messageTemplates:

private Map&lt;Locale, MessageTemplate&gt; messageTemplates;

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

    public void setMessageTemplates(Map&lt;Locale, MessageTemplate&gt; messageTemplates) {
        this.messageTemplates = messageTemplates;
    }

The json data within the Mongo collection for this field looks like:

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

When a document is retrieved from the collection,
the key in the map is an object of class String, rather than an object of type Locale. So the key of the map is converted to a String when saved to the collection, but does not seem to be mapped back to a Locale when the document is retrieved.

Our MongoClient is setup:

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(&quot;className&quot;).collectionNaming(NamingStrategy.identity()).build();

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



Does anyone have any knowledge of this scenario?

Our expectation was that the keys in the map would be of type Locale rather than type String.

We added the following hack:

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

    private Map.Entry&lt;Locale, MessageTemplate&gt; fixEntry(Map.Entry&lt;Locale, MessageTemplate&gt; 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;
    }

Which fixes our problem, but does not explain why it is occurring. As can be seen previously, we have added the LocaleCodec but it seems to be doing nothing.

答案1

得分: 0

Map键不会受到编解码的影响,出于一些技术原因。这些键会使用Conversions条目转换为/从String。现在这不完全是公共 API,但不同的用户偶尔会使用它。因为它不是公共API,所以它可能会在你使用时发生变化,但在过去几年里它一直非常稳定,所以我认为这种担忧在这一点上基本上是假设的。话虽如此,我会向你展示如何定义一个:

要注册一个转换,你需要调用Conversions.register(),它的签名如下:

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", ".")));

这个转换会接受来自数据库的String,其目标类型是URI,并调用适当的代码进行转换。在这种情况下,它是URI.create(它还会清理字符串中的转义字符)。对于Locale,你可以做类似的事情。需要注意两点:

  1. 这个转换适用于所有的String -> Locale转换,所以它需要被泛化。在这种情况下,Locale是一个非常普通的类型,所以需要特殊转换的需求几乎肯定是零。
  2. 当写入数据库时,如果找不到转换,使用的地图编解码器将简单地使用toString()方法转换键,因为数据库中的文档键只能是字符串。这可能不会产生您想要的格式,或者不能简单地传递回Locale方法以解析。如果是这种情况,您将需要在应用程序代码的某处执行register(Locale.class, String.class, l -> { /* 在此处将您的Locale转换为String */ })

这应该能帮助您解决问题。如果因为任何原因不起作用,请在 GitHub 上提交一个问题或开启一个 讨论,我会尽力帮助您解决。

英文:

I was actually fixing a bug related to this just last night and did some digging. Map keys are not subject to codec de/serialization for a number of technical reasons. These keys are converted to/from Strings using a Conversions entry. Now this isn't entirely a public API but it's been used from time to time by different users. Since it's not a public API, it might change under you but it's been pretty stable for a few years now so I think that fear is mostly hypothetical at this point. That said, I'll show you how to define one:

To register a conversion you'd call Conversions.regsiter() which has the following signature

public static &lt;S, T&gt; void register(Class&lt;S&gt; source, Class&lt;T&gt; target, Function&lt;S, T&gt; function)

And here is one of the built-in conversions:

register(String.class, URI.class, str -&gt; URI.create(str.replace(&quot;%46&quot;, &quot;.&quot;)));

This conversion will take a String coming from the database whose target type is a URI and call the appropriate code to make that conversion. In this case, it's URI.create (and it does some clean up on the escape chars in the string). For Locale you'd do something similar. Two things to note:

  1. This conversion applies to all String -> Locale conversions so it needs to be generalized. In this case, Locale is a pretty mundane type so the need for exotic and specific conversions is almost certainly 0.
  2. When writing to the database, if the conversion isn't found, the map codec being used will simply toString() the key since document keys in the database can only be strings. This may not result in a format you want or one that can simply be passed back in to a Locale method to parse back out. If that's the case, you'll want to do `register(Locale.class, String.class, l -> { /* convert your Locale to a String here /* }) somewhere in your application code.

And that should get you squared away. If that doesn't work for whatever reason, please file an issue or open a discussion on GitHub and I'll try to get you fixed up.

huangapple
  • 本文由 发表于 2023年6月5日 20:05:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76406252.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定