|
10 | 10 | import java.util.concurrent.atomic.AtomicBoolean; |
11 | 11 | import java.util.concurrent.atomic.AtomicInteger; |
12 | 12 | import java.util.concurrent.atomic.AtomicLong; |
| 13 | +import java.util.Objects; |
13 | 14 |
|
14 | 15 | import com.fasterxml.jackson.core.JsonParser.NumberType; |
15 | 16 | import com.fasterxml.jackson.core.exc.StreamWriteException; |
@@ -1968,61 +1969,94 @@ public void writeTypeId(Object id) throws IOException { |
1968 | 1969 | */ |
1969 | 1970 | public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException |
1970 | 1971 | { |
1971 | | - Object id = typeIdDef.id; |
1972 | | - |
1973 | | - final JsonToken valueShape = typeIdDef.valueShape; |
1974 | | - if (canWriteTypeId()) { |
1975 | | - typeIdDef.wrapperWritten = false; |
1976 | | - // just rely on native type output method (sub-classes likely to override) |
1977 | | - writeTypeId(id); |
1978 | | - } else { |
1979 | | - // No native type id; write wrappers |
1980 | | - // Normally we only support String type ids (non-String reserved for native type ids) |
1981 | | - String idStr = (id instanceof String) ? (String) id : String.valueOf(id); |
1982 | | - typeIdDef.wrapperWritten = true; |
1983 | | - |
1984 | | - Inclusion incl = typeIdDef.include; |
1985 | | - // first: can not output "as property" if value not Object; if so, must do "as array" |
1986 | | - if ((valueShape != JsonToken.START_OBJECT) |
1987 | | - && incl.requiresObjectContext()) { |
1988 | | - typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY; |
| 1972 | + // Are native type ids allowed? If so, use them; otherwise, use wrappers |
| 1973 | + final boolean wasStartObjectWritten = canWriteTypeId() |
| 1974 | + ? _writeTypePrefixUsingNative(typeIdDef) |
| 1975 | + : _writeTypePrefixUsingWrapper(typeIdDef); |
| 1976 | + |
| 1977 | + // And then possible start marker for value itself: |
| 1978 | + switch (typeIdDef.valueShape) { |
| 1979 | + case START_OBJECT: |
| 1980 | + if (!wasStartObjectWritten) { |
| 1981 | + writeStartObject(typeIdDef.forValue); |
1989 | 1982 | } |
| 1983 | + break; |
| 1984 | + case START_ARRAY: |
| 1985 | + writeStartArray(typeIdDef.forValue); |
| 1986 | + break; |
| 1987 | + default: // otherwise: no start marker |
| 1988 | + } |
1990 | 1989 |
|
1991 | | - switch (incl) { |
1992 | | - case PARENT_PROPERTY: |
1993 | | - // nothing to do here, as it has to be written in suffix... |
1994 | | - break; |
1995 | | - case PAYLOAD_PROPERTY: |
1996 | | - // only output as native type id; otherwise caller must handle using some |
1997 | | - // other mechanism, so... |
1998 | | - break; |
1999 | | - case METADATA_PROPERTY: |
2000 | | - // must have Object context by now, so simply write as field name |
2001 | | - // Note, too, that it's bit tricky, since we must print START_OBJECT that is part |
2002 | | - // of value first -- and then NOT output it later on: hence return "early" |
2003 | | - writeStartObject(typeIdDef.forValue); |
2004 | | - writeStringField(typeIdDef.asProperty, idStr); |
2005 | | - return typeIdDef; |
| 1990 | + return typeIdDef; |
| 1991 | + } |
2006 | 1992 |
|
2007 | | - case WRAPPER_OBJECT: |
2008 | | - // NOTE: this is wrapper, not directly related to value to output, so don't pass |
2009 | | - writeStartObject(); |
2010 | | - writeFieldName(idStr); |
2011 | | - break; |
2012 | | - case WRAPPER_ARRAY: |
2013 | | - default: // should never occur but translate as "as-array" |
2014 | | - writeStartArray(); // wrapper, not actual array object to write |
2015 | | - writeString(idStr); |
2016 | | - } |
| 1993 | + /** |
| 1994 | + * Writes a native type id (when supported by format). |
| 1995 | + * |
| 1996 | + * @return True if start of an object has been written, False otherwise. |
| 1997 | + * |
| 1998 | + * @since 2.19 |
| 1999 | + */ |
| 2000 | + protected boolean _writeTypePrefixUsingNative(WritableTypeId typeIdDef) throws IOException { |
| 2001 | + typeIdDef.wrapperWritten = false; |
| 2002 | + writeTypeId(typeIdDef.id); |
| 2003 | + return false; |
| 2004 | + } |
| 2005 | + |
| 2006 | + /** |
| 2007 | + * Writes a wrapper for the type id if necessary. |
| 2008 | + * |
| 2009 | + * @return True if start of an object has been written, false otherwise. |
| 2010 | + * |
| 2011 | + * @since 2.19 |
| 2012 | + */ |
| 2013 | + protected boolean _writeTypePrefixUsingWrapper(WritableTypeId typeIdDef) throws IOException { |
| 2014 | + // Normally we only support String type ids (non-String reserved for native type ids) |
| 2015 | + final String id = Objects.toString(typeIdDef.id, null); |
| 2016 | + |
| 2017 | + // If we don't have Type ID we don't write a wrapper. |
| 2018 | + if (id == null) { |
| 2019 | + return false; |
2017 | 2020 | } |
2018 | | - // and finally possible start marker for value itself: |
2019 | | - if (valueShape == JsonToken.START_OBJECT) { |
| 2021 | + |
| 2022 | + Inclusion incl = typeIdDef.include; |
| 2023 | + |
| 2024 | + // first: can not output "as property" if value not Object; if so, must do "as array" |
| 2025 | + if ((typeIdDef.valueShape != JsonToken.START_OBJECT) && incl.requiresObjectContext()) { |
| 2026 | + typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY; |
| 2027 | + } |
| 2028 | + |
| 2029 | + typeIdDef.wrapperWritten = true; |
| 2030 | + |
| 2031 | + switch (incl) { |
| 2032 | + case PARENT_PROPERTY: |
| 2033 | + // nothing to do here, as it has to be written in suffix... |
| 2034 | + break; |
| 2035 | + case PAYLOAD_PROPERTY: |
| 2036 | + // only output as native type id; otherwise caller must handle using some |
| 2037 | + // other mechanism, so... |
| 2038 | + break; |
| 2039 | + case METADATA_PROPERTY: |
| 2040 | + // must have Object context by now, so simply write as field name |
| 2041 | + // Note, too, that it's bit tricky, since we must print START_OBJECT that is part |
| 2042 | + // of value first -- and then NOT output it later on: hence return "early" |
2020 | 2043 | writeStartObject(typeIdDef.forValue); |
2021 | | - } else if (valueShape == JsonToken.START_ARRAY) { |
2022 | | - // should we now set the current object? |
2023 | | - writeStartArray(); |
| 2044 | + writeStringField(typeIdDef.asProperty, id); |
| 2045 | + return true; |
| 2046 | + |
| 2047 | + case WRAPPER_OBJECT: |
| 2048 | + // NOTE: this is wrapper, not directly related to value to output, so |
| 2049 | + // do NOT pass "typeIdDef.forValue" |
| 2050 | + writeStartObject(); |
| 2051 | + writeFieldName(id); |
| 2052 | + break; |
| 2053 | + case WRAPPER_ARRAY: |
| 2054 | + default: // should never occur but translate as "as-array" |
| 2055 | + writeStartArray(); // wrapper, not actual array object to write |
| 2056 | + writeString(id); |
2024 | 2057 | } |
2025 | | - return typeIdDef; |
| 2058 | + |
| 2059 | + return false; |
2026 | 2060 | } |
2027 | 2061 |
|
2028 | 2062 | /** |
|
0 commit comments