当前位置: 首页 > 知识库问答 >
问题:

使用JAXB映射对象生成非公共列表字段和使用ModelMapper丢失的setters

须鸿祯
2023-03-14

我有jaxb生成的数据结构。部分结构基本相同,但它们位于不同的名称空间中,因此生成的Java类型不同。

我需要在这些结构之间传输数据。在项目中,ModelMapper用于映射,所以我希望使用它。

我的问题是ModelMapper无法映射为'maxOccurs="unb界"元素生成的列表。

假设我有以下模式:

<xs:complexType name="CityData">
    <xs:sequence>
        <xs:element name="districtData" type="DistrictData" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="DistrictData">
    <xs:sequence>
        <xs:element name="population" type="xs:int" nillable="false" minOccurs="1" maxOccurs="1"/>
    </xs:sequence>
</xs:complexType>

我在namespacea和namespaceb中都有这个模式,因此Jaxb将以下类型生成到包namespacea和包namespaceb中:

public class CityData {
    @XmlElement(required = true)
    protected List<DistrictData> districtData;
    //... jaxb explanation why there's no setter
    public List<DistrictData> getDistrictData() {
        if (districtData == null) {
            districtData = new ArrayList<DistrictData>();
        }
        return this.districtData;
    }
}

public class DistrictData {
    protected int population;
    public int getPopulation() {
        return population;
    }
    public void setPopulation(int value) {
        this.population = value;
    }
}

现在,如果我从程序包namespacea创建一个源CityData,并要求modelmapper将其映射到namespaceb中的目标CityData,则数据不会被映射:

    CityData cityData = new CityData();
    DistrictData districtData = new DistrictData();
    districtData.setPopulation(1234);
    cityData.getDistrictData().add(districtData);

    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

    namespaceb.CityData dest = modelMapper.map(cityData, namespaceb.CityData.class);
    System.out.println("dest.districtData: " + dest.getDistrictData());

结果是:

dest.districtData: []

换句话说,districtData不会复制到目标。

我知道ModelMapper没有为districtData找到setter,因此没有对其进行映射。我了解到,可以重新配置Jaxb以生成列表属性的setter,但Jaxb对象生成并不在我的项目中。

因此,我想知道是否有一个很好的解决方案,可以使用ModelMapper进行映射,或者在这些情况下使用其他映射器库。

我创建了一个小项目:https://github.com/riskop/ModelMapperJaxb

共有2个答案

年光明
2023-03-14

我对ModelMapper的笨拙解决方法有一个粗略的想法。在阅读pirho的答案之前,转换设施。我认为皮罗的答案更好(被接受),但作为记录,下面是转换器的解决方法。这基本上是在没有设置器的情况下手动定义子结构的转换:

CountryData countryData = new CountryData();
CityData cityData = new CityData();
DistrictData districtData = new DistrictData();
districtData.setPopulation(1234);
cityData.getDistrictData().add(districtData);
countryData.getCityData().add(cityData);

ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

modelMapper.addConverter(new Converter<CountryData, namespaceb.CountryData>() {
    @Override
    public namespaceb.CountryData convert(MappingContext<CountryData, namespaceb.CountryData> context) {
        namespaceb.CountryData result = new namespaceb.CountryData();
        if(context.getSource() != null) {
            for(CityData cityData : context.getSource().getCityData()) {
                namespaceb.CityData mapped = modelMapper.map(cityData, namespaceb.CityData.class);
                result.getCityData().add(mapped);
            }
        }
        return result;
    }
});

modelMapper.addConverter(new Converter<CityData, namespaceb.CityData>() {
    @Override
    public namespaceb.CityData convert(MappingContext<CityData, namespaceb.CityData> context) {
        namespaceb.CityData result = new namespaceb.CityData();
        if(context.getSource() != null) {
            for(DistrictData districtData : context.getSource().getDistrictData()) {
                namespaceb.DistrictData mapped = modelMapper.map(districtData, namespaceb.DistrictData.class);
                result.getDistrictData().add(mapped);
            }
        }
        return result;
    }
});

namespaceb.CountryData destCountryData = modelMapper.map(countryData, namespaceb.CountryData.class);
assertEquals(1, destCountryData.getCityData().size());
namespaceb.CityData destCityData = destCountryData.getCityData().get(0);
assertEquals(1, destCityData.getDistrictData().size());
namespaceb.DistrictData destDistrictData = destCityData.getDistrictData().get(0);
assertEquals(1234, destDistrictData.getPopulation());
濮阳和泰
2023-03-14

我认为您只需要启用字段匹配,并将字段的访问级别设置为匹配,以处理缺少的setter。检查此配置

modelMapper.getConfiguration()
    .setMatchingStrategy(MatchingStrategies.STRICT)
    .setFieldMatchingEnabled(true)
    .setFieldAccessLevel(AccessLevel.PROTECTED);

Javadoc:

setFieldAccessEnabled

设置是否应启用字段匹配。如果为true,则可以在可访问字段之间进行映射。默认值为false。

setFieldAccessLevel

指示字段应符合给定访问级别的匹配条件
注意:字段访问仅在启用字段匹配时使用。

 类似资料:
  • 我的班级等级如下: 并尝试将DTO映射到实体 地图如下: 我在详细信息字段中收到带有A或B的DTO,这是在调试器中检查的。但是模型制图者投掷 无法实例化目标组织的实例。包裹基础确保该组织。包裹Base有一个非私有的无参数构造函数。 我尝试使用显式提供程序(没有用于此映射): 我还尝试像这样实现自定义转换器(也没有执行): 模型映射器似乎没有对字段使用这个类型映射,只对hierarhy的根使用。在这

  • 我正在尝试使用ModelMapper映射对象树。 我创建了一个例子来说明我的问题: 类包含多个属性 类包含类型为Sub的对象和(至少)另一个属性 类目标包含一个简单的属性列表 源属性和目标属性的类型不同 代码: 我正在寻找一种配置单个ModelMapper实例的方法,以便满足以下约束: modelMapper能够将Sub类型的对象转换为目标对象 不幸的是,行<代码>映射(source.sub,de

  • 我与ModelMapper框架有麻烦。请解释为什么我看到以下行为。 我在build.gradle有以下依赖性 和一个类客户: 我还有一个地图绘制工具: 还有一个测试 在fred()中,方法输出是非红色的“Customer{name=fred,age=40}”(“Customer{name=null,age=40}”)。你能解释一下为什么吗?为什么我在第一个方法中看不到输出“George”?

  • 首先,我有下面的发票清单。每个列表对象都有一个零件号、一个描述、数量和一个价格。 我将其映射到数量上,并将其排序到数量上,得到以下结果: 但是我如何在上进行映射,以便在我的结果中显示在所显示的数量前面?我不能这样做:

  • 问题内容: 我正在尝试整理实现公共接口的对象列表。涉及3个类和1个接口: 社区 类(具有一种方法: List getPeople(); ) 人员 接口(具有一种方法: String getName(); ) 女生 班(实施人员) 男生 班(实施人员) 请参见下面的代码。 我想要一个看起来像这样的XML: 或可能: 到目前为止,我得到的是: 我意识到我可以将元素更改为其他名称,但是我希望元素名称成为