changing mapping field for #XmlValue in #JsonProperty - java

I have an element "SubElement2" that has value and attribute.
Working :
My Annotation are
#XmlValue
#JsonProperty(value="content")
private String value;
Request in XML : <SubElement2 parameterName = "tested">ELEMENT_2_TAG_VAL</SubElement2>
TO JSON :
{
"SubElement2" : {
"content" : "ELEMENT_2_TAG_VAL",
"parameterName" : "tested"
}
}
Here I want to change the property name content to myOwnContent
Not Working:
Modified annotations :
#XmlValue
#JsonProperty(value="myOwnContent") // Modified
private String value;
Request in XML : <SubElement2 parameterName = "tested">ELEMENT_2_TAG_VAL</SubElement2>
For the above annotation change, I'm facing below exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "content" (class SubElement2), not marked as ignorable (2 known properties: "parameterName", "myOwnContent"])
at [Source: (String)"{"SubElement2":{"parameterName":"tested","content":"ELEMENT_2_TAG_VAL"}}"; line: 1, column: 53] (through reference chain: Element1["SubElement2"]->SubElement2["content"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:60)
Below is my code :
ObjectFactory.java
#XmlRegistry
public class ObjectFactory {
public SubElement2 createSubElement2() {
return new SubElement2();
}
}
MainProgram.java
public class MainProgram {
public static void main(String[] args) throws ClassNotFoundException, IOException {
String request = "<SubElement2 parameterName = \"tested\">ELEMENT_2_TAG_VAL</SubElement2>";
System.out.println("Request in XML : "+request);
JSONObject jObject = XML.toJSONObject(request);
ObjectMapper mapper = createCombinedObjectMapper();
Object json = mapper.readValue(jObject.toString(), Class.forName("Element1"));
String output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
System.out.println(output);
}
private static ObjectMapper createCombinedObjectMapper() {
return new ObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, false)
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
.setAnnotationIntrospector(createJaxbJacksonAnnotationIntrospector());
}
private static AnnotationIntrospector createJaxbJacksonAnnotationIntrospector() {
final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
final AnnotationIntrospector jacksonIntrospector = new JacksonAnnotationIntrospector();
return AnnotationIntrospector.pair(jacksonIntrospector, jaxbIntrospector);
}
}
SubElement2.java
#XmlRootElement(name = "SubElement2")
public class SubElement2 {
#XmlValue
#JsonProperty(value="myOwnContent")
private String value;
#JsonProperty(value="parameterName",required=true)
#XmlAttribute(required = true)
protected String parameterName;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getParameterName() {
return parameterName;
}
public void setParameterName(String parameterName) {
this.parameterName = parameterName;
}
}
Element1.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "element1", propOrder = {
"element1"
})
#XmlRootElement(name = "Element1")
public class Element1 {
#XmlElement(name="SubElement2")
protected SubElement2 subElement2;
public SubElement2 getSubElement2() {
return subElement2;
}
public void setSubElement2(SubElement2 subElement2) {
this.subElement2 = subElement2;
}
}
I'm using Jackson version : 2.9.7
Help me to solve this exception ,thanks in advance
What I want to do :
I want to have jsonproperty as myOwnContent because content is already used by another attribute in XML in my existing implementation. (Existing implementation doesn't have JSON suuport , we are adding JSON support)

Related

Jackson: How to serialize Object values in a Map with type information inside key object?

Edit:
The problem could be isolated and is described here Jackson: Deserialize to a Map<String, Object> with correct type for each value
First off, there is a work around using enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY);. But that is not really applicable because the method is deprecated for obvious reasons and it also adds unnecessary type information.
Edit:
I stripped down the code to a bare minimum and I also found the problem. The container has Object as values which Jackson seems to treat as simple Object during serialization. That might be the reason why the annotations at class level are not being considered.
#JsonProperty("simulation_parameters")
private final Map<Parameter<?>, Object> simulation_parameters;
The resulting JSON looks like this after serialization:
{
"simulation_parameters" : {
"INPUT_HOUSEHOLDS" : "input_hh",
"INPUT_PERSONS" : "input_p"
}
}
Changing the container to:
#JsonProperty("simulation_parameters")
private final Map<Parameter<?>, DataResource> simulation_parameters;
Results in:
{
"simulation_parameters" : {
"INPUT_HOUSEHOLDS" : {
"#type" : "database",
"resource_name" : "public.households"
},
"INPUT_PERSONS" : {
"#type" : "file",
"filename" : "d:/persons.csv"
}
}
}
Since I have type information in my map keys, is there a way to serialize an entire entry instead of serializing key and value seperately using the #JsonSerialize(keyUsing:..., contentUsing:...)?
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.util.*;
public class JacksonTest {
public static void main(String[] args) throws IOException {
ParameterContainer parameters = new ParameterContainer();
DataResource input_hh = new DatabaseResource("public.households");
parameters.addParameter(Parameters.INPUT_HOUSEHOLDS, input_hh);
DataResource input_p = new FileResource("d:/persons.csv");
parameters.addParameter(Parameters.INPUT_PERSONS, input_p);
ObjectMapper om = new ObjectMapper();
String json_missing_type = om.writerWithDefaultPrettyPrinter().writeValueAsString(parameters);
System.out.println(json_missing_type);
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY, //apparently this is redundant
property = "#type")
#JsonSubTypes({
#JsonSubTypes.Type(value = DatabaseResource.class, name = "database"),
#JsonSubTypes.Type(value = FileResource.class, name = "file")
})
public interface DataResource {
#JsonIgnore
String getResourceName();
}
//data resource as file
#JsonTypeName("file")
public static class FileResource implements DataResource {
#JsonProperty("filename")
private final String filename;
#JsonCreator
public FileResource( #JsonProperty("filename") String filename) {
this.filename = filename;
}
#JsonIgnore
#Override
public String getResourceName() {
return this.filename;
}
}
//data resource as database
#JsonTypeName("database")
public static class DatabaseResource implements DataResource {
#JsonProperty("resource_name")
private final String resource_name;
#JsonCreator
public DatabaseResource(#JsonProperty("resource_name") String resource_name) {
this.resource_name = resource_name;
}
#JsonIgnore
#Override
public String getResourceName() {
return resource_name;
}
}
public static class ParameterDeserializer extends KeyDeserializer {
#Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
Optional<Parameter<?>> defined_parameter = Parameters.values.stream().filter(param -> param.getName().equals(key)).findAny();
if (defined_parameter.isPresent())
return defined_parameter.get();
else
throw new IllegalArgumentException("Parameter: " + key + " is not defined");
}
}
public static class ParameterContainer {
#JsonProperty("simulation_parameters")
#JsonSerialize(typing = JsonSerialize.Typing.DEFAULT_TYPING)
private final Map<Parameter<?>, DataResource> simulation_parameters;
public ParameterContainer() {
this.simulation_parameters = new HashMap<>();
}
#JsonCreator
public ParameterContainer(#JsonProperty("simulation_parameters") Map<Parameter<?>,DataResource> simulation_parameters) {
this.simulation_parameters = simulation_parameters;
}
#JsonIgnore
public void addParameter(Parameter<?> parameter,DataResource value) {
simulation_parameters.put(parameter, value);
}
}
public static class Parameter<T> {
#JsonValue
private final String name;
private final Class<T> object_type;
protected Parameter(String name, Class<T> object_type) {
this.name = name;
this.object_type = object_type;
}
public String getName() {
return name;
}
#JsonIgnore
public Class<T> getObjectType() {
return object_type;
}
}
public static final class Parameters {
public static final Parameter<DataResource> INPUT_HOUSEHOLDS = new Parameter<>("INPUT_HOUSEHOLDS", DataResource.class);
public static final Parameter<DataResource> INPUT_PERSONS = new Parameter<>("INPUT_PERSONS", DataResource.class);
public static final Set<Parameter<?>> values = Set.of(INPUT_HOUSEHOLDS, INPUT_PERSONS);
}
}

Deserialize json array values to specific Java class fields

I have the following Json
{
"coreId" : "1",
"name" : "name",
"additionalValueList" : [
{
"columnName" : "allow_duplicate",
"rowId" : "10",
"value" : "1"
},
{
"columnName" : "include_in_display",
"rowId" : "11",
"value" : "0"
},
...e.t.c
]
},
...e.t.c
and Java class
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
private Boolean allowDuplicate;
private Boolean includeInDisplay;
}
How I can easily map values from 'additionalValueList' to corresponding java fields.For example Json value from field 'columnName' - 'allow_duplicate' = DTO.allowDuplicate.
Actually I know how to do it with custom deserializers with #JsonDeserialize annotation and smth like this.Bu I have 40+ DTO and it is not a good idea to create own deserializer for each filed. I am looking for solution to have for example 1 deserializer(since values structure in 'additionalValueList' are the same for all entities) and to pass parameter(field name that I want to map to that field) to custom deserializer that will find in 'additionalValueList' entity with 'column Name' = parameter(that I passed from annotation) and return 'value'.
Example
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
#JsonDeserialize(using = MyCustDeser.class,param = allow_duplicate)
private Boolean allowDuplicate;
#JsonDeserialize(using = MyCustDeser.class,param = include_in_display)
private Boolean includeInDisplay;
}
It will be a good solution but maybe not easy to achieve.However I will be very grateful for all your advices.Thank you.
Create a Converter class, then specify it on the DTO class.
The following code uses public fields for the simplicity of the example.
/**
* Intermediate object used for deserializing FooDto from JSON.
*/
public final class FooJson {
/**
* Converter used when deserializing FooDto from JSON.
*/
public static final class ToDtoConverter extends StdConverter<FooJson, FooDto> {
#Override
public FooDto convert(FooJson json) {
FooDto dto = new FooDto();
dto.name = json.name;
dto.id = json.coreId;
dto.allowDuplicate = lookupBoolean(json, "allow_duplicate");
dto.includeInDisplay = lookupBoolean(json, "include_in_display");
return dto;
}
private static Boolean lookupBoolean(FooJson json, String columnName) {
String value = lookup(json, columnName);
return (value == null ? null : (Boolean) ! value.equals("0"));
}
private static String lookup(FooJson json, String columnName) {
if (json.additionalValueList != null)
for (FooJson.Additional additional : json.additionalValueList)
if (columnName.equals(additional.columnName))
return additional.value;
return null;
}
}
public static final class Additional {
public String columnName;
public String rowId;
public String value;
}
public Integer coreId;
public String name;
public List<Additional> additionalValueList;
}
You now simply annotate the DTO to use it:
#JsonDeserialize(converter = FooJson.ToDtoConverter.class)
public final class FooDto {
public Integer id;
public String name;
public Boolean allowDuplicate;
public Boolean includeInDisplay;
#Override
public String toString() {
return "FooDto[id=" + this.id +
", name=" + this.name +
", allowDuplicate=" + this.allowDuplicate +
", includeInDisplay=" + this.includeInDisplay + "]";
}
}
Test
ObjectMapper mapper = new ObjectMapper();
FooDto foo = mapper.readValue(new File("test.json"), FooDto.class);
System.out.println(foo);
Output
FooDto[id=1, name=name, allowDuplicate=true, includeInDisplay=false]

Eclipse MOXy Unmarshall Exception: Missing class indicator field from database row

I am having issue while unmarshalling an XML message. However, marshaling works fine. I am using inheritance via XML class extractor annotation.
Can you please help me identify the issue here?
Exception:
Caused by: Exception [EclipseLink-44] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Missing class indicator field from database row [org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl#631330c].
Descriptor: XMLDescriptor(main.TransitionGuard --> [])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassIndicatorField(DescriptorException.java:961)
at org.eclipse.persistence.internal.oxm.XMLRelationshipMappingNodeValue.processChild(XMLRelationshipMappingNodeValue.java:85)
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.startElement(XMLCompositeObjectMappingNodeValue.java:385)
at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.startElement(UnmarshalRecordImpl.java:864)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parseEvent(XMLStreamReaderReader.java:138)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:102)
at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:89)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:940)
at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:655)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:637)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:216)
... 1 more
Here are my classes:
Transition
public class Transition {
private String from;
private String to;
TransitionGuard guard;
public Transition() {
// For Moxy
}
public Transition(String from, String to) {
this(from, to, null);
}
public Transition(String from, String to, TransitionGuard guard) {
setFrom(from);
setTo(to);
setTransitionGuard(guard);
}
#XmlAttribute(name = "from")
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
#XmlAttribute(name = "to")
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
#XmlElement(name = "guard")
public TransitionGuard getTransitionGuard() {
return guard;
}
public void setTransitionGuard(TransitionGuard guard) {
this.guard = guard;
}
}
TransitionGuard
#XmlClassExtractor(TransitionGuardClassExtractor.class)
#XmlSeeAlso({ ExceptionGuard.class, ScriptedGuard.class })
public abstract class TransitionGuard {
}
TransitionGuardClassExtractor
public class TransitionGuardClassExtractor extends ClassExtractor {
#Override
public Class extractClassFromRow(Record record, Session session) {
if (null != record.get("/abpm:exception-guard")) {
return ExceptionGuard.class;
} else if (null != record.get("/abpm:scripted-guard")) {
return ScriptedGuard.class;
}
return null;
}
}
ScriptedGuard
public class ScriptedGuard extends TransitionGuard {
String script;
public ScriptedGuard() {
}
public ScriptedGuard(String script) {
setScript(script);
}
#XmlPath("abpm:scripted-guard/text()")
#XmlCDATA
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
}
TestPE
#XmlRootElement(name = "TestPE")
public class TestPE {
List<Transition> transition;
public List<Transition> getTransition() {
return transition;
}
public void setTransition(List<Transition> transition) {
this.transition = transition;
}
}
package-info
#XmlSchema(namespace = "jelly:com.werken.blissed.jelly.BlissedTagLibrary", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, xmlns = {
#XmlNs(prefix = "version", namespaceURI = "4.7"), #XmlNs(prefix = "j", namespaceURI = "jelly:core"),
#XmlNs(prefix = "abpm", namespaceURI = "jelly:com.adeptia.indigo.jelly.IndigoTagLibrary"),
#XmlNs(prefix = "pd", namespaceURI = "jelly:com.adeptia.indigo.pd.ProcessDesignerTagLibrary"),
#XmlNs(prefix = "", namespaceURI = "jelly:com.werken.blissed.jelly.BlissedTagLibrary") })
package main;
import javax.xml.bind.annotation.XmlNs;
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<TestPE xmlns:version = "4.7" xmlns:j = "jelly:core" xmlns:abpm = "jelly:com.adeptia.indigo.jelly.IndigoTagLibrary" xmlns:pd = "jelly:com.adeptia.indigo.pd.ProcessDesignerTagLibrary" xmlns = "jelly:com.werken.blissed.jelly.BlissedTagLibrary" >
<transition from="state-BPMN:GATEWAY:XOR_DATA_GATEWAY-892276265" to="state-BPMN:EVENT:END_EVENT-892276264" >
<guard>
<abpm:scripted-guard><![CDATA[*** Script ***]]></abpm:scripted-guard>
</guard>
</transition>
</TestPE>
Demo code
Map<String, String> properties = new HashMap<String, String>();
properties.put("javax.xml.bind.context.factory", "org.eclipse.persistence.jaxb.JAXBContextFactory");
JAXBContext jc = JAXBContext.newInstance(new Class[] { TestPE.class }, properties);
// JAXB unmarshall
Unmarshaller unmarshaller = jc.createUnmarshaller();
TestPE testPE = (TestPE) unmarshaller.unmarshal(new File("resources/Transition.xml"));
Currently, MOXy requires that the inheritance indicator is in an XML attribute. If it is in an XML element you could use the following approach with an XmlAdapter.

Make input parameter mandatory JAX-WS

Input paramter to my webservice method is an Object of Class AddSingleDocRequest. This class contains all the input fields as class instance variable with their getter and setter. I want to make some of the input fields mandatory. What is the best way to achieve this ?
Following is the code snippet:
**//webservice method
public String uploadDoc(AddSingleDocRequest request)
{
}
**//Request Class**
public class AddSingleDocRequest
{
private String sFilepath;
private String sDataClass;
public void setDataClassName(String dataClassName)
{
this.sDataClass= dataClassName;
}
public String getDataClassName() {
return sDataClass;
}
public void setFilePath(String filePath)
{
this.sFilepath=filePath;
}
public String getFilePath()
{
return sFilepath;
}
}
I want to make sFilePath parameter as mandatory.
Add the next JAX-B annotations:
#XmlType(name = "AddSingleDocRequestType", propOrder = {
"sFilepath", "sDataClass"
})
public class AddSingleDocRequest {
#XmlElement(name = "sFilepath", required = true)
private String sFilepath;
#XmlElement(name = "sDataClass", required = false)
private String sDataClass;
public void setDataClassName(String dataClassName) {
this.sDataClass = dataClassName;
}
public String getDataClassName() {
return sDataClass;
}
public void setFilePath(String filePath) {
this.sFilepath = filePath;
}
public String getFilePath() {
return sFilepath;
}
}
See more in Using JAXB to customize mapping for JAX-WS web services.

Jackson use custom JSON deserializer with default

All code below is simplified version.
I have JSON structure:
{
"content" : {
"elements" : [ {
"type" : "simple"
},
{
"type" : "complex",
"content" : {
"elements" : [ {
"type" : "simple"
},
{
"type" : "simple"
},
{
"type" : "complex",
"content" : {
---- /// ----
}
} ]
}
} ]
}
}
I use Jackson lib for deserialization, and i am trying to implement a kind of "mix" custom with default deserializers.
I want Element object creates using custom ElementDeserializer but for Content field inside use default. Unfortunately things like that:
#JsonDeserialize(using = StdDeserializer.class)
#JsonProperty
Content content;
isn't work =(
Here is my code now:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Content {
#JsonProperty("elements")
ArrayList<Element> mElements;
}
#JsonDeserialize(using = ElementDeserializer.class)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Element<T extends ElementType> {
#JsonProperty
Content content;
T mField;
public Element(T field) {
mField = field;
}
}
public class ElementDeserializer extends JsonDeserializer<Element> {
#Override
public Element deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Element element = null;
JsonNode node = jp.getCodec().readTree(jp);
if ("simple".equals(node.get("type").textValue())) {
element = new Element(new SimpleField());
} else if ("complex".equals(node.get("type").textValue())) {
element = new Element(new ComplexField());
}
return element;
}
}
I will be grateful for some help!
Not sure whether it is mandatory for you to use a custom deserializer (for reasons not indicated in your post). If it is not, then you can do without one, using the default deserializers.
Here is how:
#JsonIgnoreProperties(ignoreUnknown = true)
public class TopObject {
#JsonProperty
public Content content;
public TopObject() {
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Content {
#JsonProperty
public Element elements [];
public Content() {
}
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#Type(value = SimpleElement.class, name = "simple"),
#Type(value = ComplexElement.class, name = "complex")
})
public class Element {
public Element() {
}
}
public class SimpleElement extends Element {
public SimpleElement() {
}
}
public class ComplexElement extends Element {
#JsonProperty
public Content content;
public ComplexElement() {
}
}
Then unserialize the json data as a TopObject.class

Categories

Resources