StackTraceElementDeserializer.java
package tools.jackson.databind.deser.jdk;
import java.util.ArrayList;
import java.util.List;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonParser;
import tools.jackson.core.JsonToken;
import tools.jackson.databind.*;
import tools.jackson.databind.deser.SettableBeanProperty;
import tools.jackson.databind.deser.bean.BeanDeserializerBase;
import tools.jackson.databind.deser.bean.BeanPropertyMap;
import tools.jackson.databind.deser.std.StdScalarDeserializer;
import tools.jackson.databind.introspect.BeanPropertyDefinition;
public class StackTraceElementDeserializer
extends StdScalarDeserializer<StackTraceElement>
{
protected final ValueDeserializer<?> _adapterDeserializer;
protected StackTraceElementDeserializer(ValueDeserializer<?> ad)
{
super(StackTraceElement.class);
_adapterDeserializer = ad;
}
public static ValueDeserializer<?> construct(DeserializationContext ctxt) {
// 27-May-2022, tatu: MUST contextualize, alas, for optimized bean property
// matching to work
ValueDeserializer<?> adapterDeser = ctxt.findRootValueDeserializer(ctxt.constructType(Adapter.class));
// [databind#429]: Check for mix-in @JsonProperty name overrides on
// StackTraceElement and propagate as aliases to the Adapter deserializer
if (adapterDeser instanceof BeanDeserializerBase beanDeser) {
Class<?> mixin = ctxt.getConfig().findMixInClassFor(StackTraceElement.class);
if (mixin != null) {
adapterDeser = _applyPropertyAliases(ctxt, beanDeser);
}
}
return new StackTraceElementDeserializer(adapterDeser);
}
/**
* Introspects {@code StackTraceElement} properties (including mix-ins) for
* any {@code @JsonProperty} name overrides; if found, injects them as aliases
* into the Adapter's {@link BeanPropertyMap} so that renamed properties are
* recognized without any token-stream rewriting.
*/
private static ValueDeserializer<?> _applyPropertyAliases(DeserializationContext ctxt,
BeanDeserializerBase adapterDeser)
{
JavaType steType = ctxt.constructType(StackTraceElement.class);
List<BeanPropertyDefinition> steDefs = ctxt.introspectBeanDescription(steType).findProperties();
List<SettableBeanProperty> adapterProps = new ArrayList<>();
adapterDeser.properties().forEachRemaining(adapterProps::add);
// For each STE property where mix-in renamed it (external != internal),
// find the matching Adapter property and register the external name as alias
PropertyName[][] aliasDefs = null;
for (BeanPropertyDefinition steProp : steDefs) {
String externalName = steProp.getName();
String internalName = steProp.getInternalName();
if (externalName.equals(internalName)) {
continue;
}
for (int i = 0, end = adapterProps.size(); i < end; ++i) {
if (internalName.equals(adapterProps.get(i).getName())) {
if (aliasDefs == null) {
aliasDefs = new PropertyName[end][];
}
aliasDefs[i] = new PropertyName[] { PropertyName.construct(externalName) };
break;
}
}
}
if (aliasDefs == null) {
return adapterDeser;
}
boolean caseInsensitive = ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
BeanPropertyMap newMap = BeanPropertyMap.construct(
ctxt.getConfig(), adapterProps, aliasDefs, caseInsensitive)
.initMatcher(ctxt.tokenStreamFactory());
return adapterDeser.withBeanProperties(newMap);
}
@Override
public StackTraceElement deserialize(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
JsonToken t = p.currentToken();
// Must get an Object
if (t == JsonToken.START_OBJECT || t == JsonToken.PROPERTY_NAME) {
return constructValue(ctxt, (Adapter) _adapterDeserializer.deserialize(p, ctxt));
}
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final StackTraceElement value = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
handleMissingEndArrayForSingle(p, ctxt);
}
return value;
}
return (StackTraceElement) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
}
protected StackTraceElement constructValue(DeserializationContext ctxt,
Adapter adapted)
{
return constructValue(ctxt, adapted.className, adapted.methodName,
adapted.fileName, adapted.lineNumber,
adapted.moduleName, adapted.moduleVersion,
adapted.classLoaderName);
}
/**
* Overridable factory method used for constructing {@link StackTraceElement}s.
*/
protected StackTraceElement constructValue(DeserializationContext ctxt,
String className, String methodName, String fileName, int lineNumber,
String moduleName, String moduleVersion, String classLoaderName)
{
// 21-May-2016, tatu: With Java 9, could use different constructor, probably
// via different module, and throw exception here if extra args passed
// 08-Dec-2024, tatu: With Jackson 3.0 can use full Java 9 introduced
// constructor, finally
return new StackTraceElement(classLoaderName, moduleName, moduleVersion,
className, methodName, fileName, lineNumber);
}
/**
* Intermediate class used both for convenience of binding and
* to support {@code PropertyNamingStrategy}.
*<p>
* NOTE: MUST remain {@code public} for JDK 17 at least to avoid
* needing opening up access separately.
*/
public final static class Adapter {
// NOTE: some String fields must not be nulls
public String className = "", classLoaderName;
public String declaringClass, format;
public String fileName = "", methodName = "";
public int lineNumber = -1;
public String moduleName, moduleVersion;
public boolean nativeMethod;
}
}