JAXBElement:为类 java.lang.Class 提供编解码器(/converter?) [英] JAXBElement: providing codec (/converter?) for class java.lang.Class

查看:20
本文介绍了JAXBElement:为类 java.lang.Class 提供编解码器(/converter?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在评估将 spring-data-mongodb 用于

这里需要注意的是

我尝试按照说明使用 spring-data-mongodb 转换器 here 但是,似乎我遗漏了一些东西,因为例外是关于编解码器"而不是转换器".

感谢任何帮助.

为 JAXBElement 添加转换器

注意: 适用于 org.springframework.boot::spring-boot-starter-parent 的 1.5.6.RELEASE 版本.使用 2.0.0.M3 版本,地狱崩溃

似乎我之前尝试添加转换器时遗漏了一些东西.所以,我像下面这样添加它进行测试:

@Component@ReadingConverter公共类 JAXBElementReadConverter 实现了 Converter{//@自动连线//MongoConverter 转换器;@覆盖公共 JAXBElement 转换(DBObject dbObject){类声明类型,范围;QName name = qNameFromString((String)dbObject.get("name"));Object rawValue = dbObject.get("value");试试{声明类型 = Class.forName((String)dbObject.get("declaredType"));} catch (ClassNotFoundException e) {如果 (rawValue.getClass().isArray()) 声明类型 = List.class;else 声明类型 = LinkedHashMap.class;}试试{scope = Class.forName((String) dbObject.get("scope"));} catch (ClassNotFoundException e) {范围 = JAXBElement.GlobalScope.class;}//Object value = rawValue instanceof DBObject ?Converter.read(declaredType, (DBObject) rawValue) : rawValue;对象值 = "TODO";返回新的 JAXBElement(名称、声明类型、范围、值);}QName qNameFromString(String s) {String[] 部分 = s.split("[{}]");if (parts.length > 2) return new QName(parts[1],parts[2],parts[0]);if (parts.length == 1) return new QName(parts[0]);return new QName("undef");}}@组件@WritingConverter公共类 JAXBElementWriteConverter 实现 Converter{//@自动连线//MongoConverter 转换器;@覆盖公共 DBObject 转换(JAXBElement jaxbElement){DBObject dbObject = new BasicDBObject();dbObject.put("name", qNameToString(jaxbElement.getName()));dbObject.put("declaredType", jaxbElement.getDeclaredType().getName());dbObject.put("scope", jaxbElement.getScope().getCanonicalName());//dbObject.put("value", converter.convertToMongoType(jaxbElement.getValue()));dbObject.put("value", "TODO");dbObject.put("_class", JAXBElement.class.getName());返回数据库对象;}公共字符串 qNameToString(QName 名称) {if (name.getNamespaceURI() == XMLConstants.NULL_NS_URI) 返回 name.getLocalPart();return name.getPrefix() + '{' + name.getNamespaceURI() + '}' + name.getLocalPart();}}@SpringBootApplication公共类 TsdApplication {公共静态无效主(字符串 [] args){SpringApplication.run(TsdApplication.class, args);}@豆公共 CustomConversions customConversions() {返回新的 CustomConversions(Arrays.asList(新的 JAXBElementReadConverter(),新的 JAXBElementWriteConverter()));}}

到目前为止一切顺利.但是,如何实例化 MongoConverter converter;?MongoConverter 是一个接口,所以我想我需要一个遵守这个接口的可实例化类.有什么建议吗?

解决方案

我理解人们希望能够将现有的域对象映射到数据库层而无需样板,但即使您没有JAXB 类结构问题,我仍然建议不要逐字使用它.除非这是一个简单的一次性项目,否则您几乎肯定会遇到需要更改域模型但您的持久数据需要保持现有状态的地步.如果您只是直接持久化数据,则没有机制可以在较新的域架构和较旧的持久化数据方案之间进行转换.持久化数据方案的版本控制也是明智之举.

您发布的用于编写客户转换器的链接是实现这一目标的一种方式,并且非常适合 Spring 生态系统.该方法还应该解决您遇到的问题(关于底层凌乱的 JAXB 数据结构不能干净地转换).

您无法使用该方法吗?确保您使用 @Component 加上自动类扫描或通过某些 Configuration 类手动将它们加载到 Spring 上下文中.

EDIT 以解决您的

将以下内容添加到您的每个转换器中:

私有最终 MongoConverter 转换器;公共 JAXBElement____Converter(MongoConverter 转换器) {this.converter = 转换器;}

尝试将您的 bean 定义更改为:

@Beanpublic CustomConversions customConversions(@Lazy MongoConverter 转换器) {返回新的 CustomConversions(Arrays.asList(新的 JAXBElementReadConverter(converter),新的 JAXBElementWriteConverter(converter)));}

I have been evaluating to adopt spring-data-mongodb for a project. In summary, my aim is:

  1. Using existing XML schema files to generate Java classes.

The thing to note here is that ExtensionType contains protected List<Object> any; allowing it to store Objects of any class. In my case, it is amongst the classes named TSDModule_Name_HereModuleType and can be browsed here

  1. Use spring-data-mongodb as persistence store

    • This is achieved using a simple ProductDataRepository

      @RepositoryRestResource(collectionResourceRel = "product", path = "product")
      public interface ProductDataRepository extends MongoRepository<TSDProductDataType, String> {
          TSDProductDataType queryByGtin(@Param("gtin") String gtin);
      }
      

    • The unmarshalled TSDProductDataType, however, contains JAXBElement which spring-data-mongodb doesn't seem to handle by itself and throws a CodecConfigurationException org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.

Here is the faulty statement:

TSDProductDataType tsdProductDataType = jaxbElement.getValue();
repository.save(tsdProductDataType);

I tried playing around with Converters for spring-data-mongodb as explained here, however, it seems I am missing something since the exception is about "Codecs" and not "Converters".

Any help is appreciated.

EDIT:

Adding converters for JAXBElement

Note: Works with version 1.5.6.RELEASE of org.springframework.boot::spring-boot-starter-parent. With version 2.0.0.M3, hell breaks loose

It seems that I missed something while trying to add converter earlier. So, I added it like below for testing:

@Component
@ReadingConverter
public class JAXBElementReadConverter implements Converter<DBObject, JAXBElement> {
    //@Autowired
    //MongoConverter converter;

    @Override
    public JAXBElement convert(DBObject dbObject) {
        Class declaredType, scope;
        QName name = qNameFromString((String)dbObject.get("name"));
        Object rawValue = dbObject.get("value");
        try {
            declaredType = Class.forName((String)dbObject.get("declaredType"));
        } catch (ClassNotFoundException e) {
            if (rawValue.getClass().isArray()) declaredType = List.class;
            else declaredType = LinkedHashMap.class;
        }
        try {
            scope = Class.forName((String) dbObject.get("scope"));
        } catch (ClassNotFoundException e) {
            scope = JAXBElement.GlobalScope.class;
        }
        //Object value = rawValue instanceof DBObject ? converter.read(declaredType, (DBObject) rawValue) : rawValue;
        Object value = "TODO";
        return new JAXBElement(name, declaredType, scope, value);
    }

    QName qNameFromString(String s) {
        String[] parts = s.split("[{}]");
        if (parts.length > 2) return new QName(parts[1], parts[2], parts[0]);
        if (parts.length == 1) return new QName(parts[0]);
        return new QName("undef");
    }
}


@Component
@WritingConverter
public class JAXBElementWriteConverter implements Converter<JAXBElement, DBObject> {
    //@Autowired
    //MongoConverter converter;

    @Override
    public DBObject convert(JAXBElement jaxbElement) {
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name", qNameToString(jaxbElement.getName()));
        dbObject.put("declaredType", jaxbElement.getDeclaredType().getName());
        dbObject.put("scope", jaxbElement.getScope().getCanonicalName());
        //dbObject.put("value", converter.convertToMongoType(jaxbElement.getValue()));
        dbObject.put("value", "TODO");
        dbObject.put("_class", JAXBElement.class.getName());
        return dbObject;
    }

    public String qNameToString(QName name) {
        if (name.getNamespaceURI() == XMLConstants.NULL_NS_URI) return name.getLocalPart();
        return name.getPrefix() + '{' + name.getNamespaceURI() + '}' + name.getLocalPart();
    }
}


@SpringBootApplication
public class TsdApplication {

    public static void main(String[] args) {
        SpringApplication.run(TsdApplication.class, args);
    }

    @Bean
    public CustomConversions customConversions() {
        return new CustomConversions(Arrays.asList(
                new JAXBElementReadConverter(),
                new JAXBElementWriteConverter()
        ));
    }
}

So far so good. However, how do I instantiate MongoConverter converter;? MongoConverter is an interface so I guess I need an instantiable class adhering to this interface. Any suggestions?

解决方案

I understand the desire for convenience in being able to just map an existing domain object to the database layer with no boilerplate, but even if you weren't having the JAXB class structure issue, I would still be recommending away from using it verbatim. Unless this is a simple one-off project, you almost definitely will hit a point where your domain models will need to change but your persisted data need to remain in an existing state. If you are just straight persisting the data, you have no mechanism to convert between a newer domain schema and an older persisted data scheme. Versioning of the persisted data scheme would be wise too.

The link you posted for writing the customer converters is one way to achieve this and fits in nicely with the Spring ecosystem. That method should also solve the issue you are experiencing (about the underlying messy JAXB data structure not converting cleanly).

Are you unable to get that method working? Ensure you are loading them into the Spring context with @Component plus auto-class scanning or manually via some Configuration class.

EDIT to address your EDIT:

Add the following to each of your converters:

private final MongoConverter converter;

public JAXBElement____Converter(MongoConverter converter) {
    this.converter = converter;
}

Try changing your bean definition to:

@Bean
public CustomConversions customConversions(@Lazy MongoConverter converter) {
    return new CustomConversions(Arrays.asList(
            new JAXBElementReadConverter(converter),
            new JAXBElementWriteConverter(converter)
    ));
}

这篇关于JAXBElement:为类 java.lang.Class 提供编解码器(/converter?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆