55package org .opensearch .dataprepper .plugins .codec .parquet ;
66
77import java .math .BigDecimal ;
8+ import java .math .BigInteger ;
89import java .nio .ByteBuffer ;
910import java .nio .charset .StandardCharsets ;
1011import java .util .Collection ;
1516import java .util .function .Function ;
1617
1718import org .apache .avro .LogicalType ;
19+ import org .apache .avro .LogicalTypes ;
1820import org .apache .avro .Schema ;
1921import org .apache .avro .generic .GenericContainer ;
2022import org .apache .avro .generic .GenericData ;
@@ -50,7 +52,7 @@ public Function<Object, Object> getLogicalTypeConverter(LogicalType logicalType)
5052
5153 public String serialize (GenericRecord value ) {
5254 StringBuilder buffer = new StringBuilder ();
53- serialize (value , buffer , new IdentityHashMap <>(128 ) );
55+ serialize (value , buffer , new IdentityHashMap <>(128 ), null );
5456 String result = buffer .toString ();
5557 return result ;
5658 }
@@ -59,7 +61,9 @@ public String serialize(GenericRecord value) {
5961 " \" >>> CIRCULAR REFERENCE CANNOT BE PUT IN JSON STRING, ABORTING RECURSION <<<\" " ;
6062
6163 /** Renders a Java datum as <a href="http://www.json.org/">JSON</a>. */
62- private void serialize (final Object datum , final StringBuilder buffer , final IdentityHashMap <Object , Object > seenObjects ) {
64+ private void serialize (final Object datum , final StringBuilder buffer ,
65+ final IdentityHashMap <Object , Object > seenObjects ,
66+ final Integer decimalScale ) {
6367 if (isRecord (datum )) {
6468 if (seenObjects .containsKey (datum )) {
6569 buffer .append (TOSTRING_CIRCULAR_REFERENCE_ERROR_TEXT );
@@ -70,10 +74,32 @@ private void serialize(final Object datum, final StringBuilder buffer, final Ide
7074 int count = 0 ;
7175 Schema schema = getRecordSchema (datum );
7276 for (Schema .Field f : schema .getFields ()) {
73- serialize (f .name (), buffer , seenObjects );
77+ serialize (f .name (), buffer , seenObjects , null ); // field name
7478 buffer .append (": " );
79+
7580 Function <Object , Object > logicalTypeConverter = getLogicalTypeConverter (f );
76- serialize (logicalTypeConverter .apply (getField (datum , f .name (), f .pos ())), buffer , seenObjects );
81+
82+ boolean serializedDecimal = false ;
83+ Schema fieldSchema = f .schema ();
84+ if (fieldSchema .getType () == Schema .Type .UNION ) {
85+ for (Schema s : fieldSchema .getTypes ()) {
86+ if (s .getType () != Schema .Type .NULL ) {
87+ if (s .getType () == Schema .Type .BYTES &&
88+ s .getLogicalType () instanceof LogicalTypes .Decimal ) {
89+ serialize (logicalTypeConverter .apply (getField (datum , f .name (), f .pos ())), buffer , seenObjects , ((LogicalTypes .Decimal ) s .getLogicalType ()).getScale ());
90+ serializedDecimal = true ;
91+ break ;
92+ }
93+ }
94+ }
95+ } else if (fieldSchema .getLogicalType () instanceof LogicalTypes .Decimal ) {
96+ serialize (logicalTypeConverter .apply (getField (datum , f .name (), f .pos ())), buffer , seenObjects , ((LogicalTypes .Decimal ) fieldSchema .getLogicalType ()).getScale ());
97+ serializedDecimal = true ;
98+ }
99+
100+ if (!serializedDecimal ) {
101+ serialize (logicalTypeConverter .apply (getField (datum , f .name (), f .pos ())), buffer , seenObjects , null );
102+ }
77103 if (++count < schema .getFields ().size ())
78104 buffer .append (", " );
79105 }
@@ -90,7 +116,7 @@ private void serialize(final Object datum, final StringBuilder buffer, final Ide
90116 long last = array .size ()-1 ;
91117 int i = 0 ;
92118 for (Object element : array ) {
93- serialize (element , buffer , seenObjects );
119+ serialize (element , buffer , seenObjects , null );
94120 if (i ++ < last )
95121 buffer .append (", " );
96122 }
@@ -107,9 +133,9 @@ private void serialize(final Object datum, final StringBuilder buffer, final Ide
107133 @ SuppressWarnings (value ="unchecked" )
108134 Map <Object ,Object > map = (Map <Object ,Object >)datum ;
109135 for (Map .Entry <Object ,Object > entry : map .entrySet ()) {
110- serialize (entry .getKey (), buffer , seenObjects );
136+ serialize (entry .getKey (), buffer , seenObjects , null );
111137 buffer .append (": " );
112- serialize (entry .getValue (), buffer , seenObjects );
138+ serialize (entry .getValue (), buffer , seenObjects , null );
113139 if (++count < map .size ())
114140 buffer .append (", " );
115141 }
@@ -120,15 +146,24 @@ private void serialize(final Object datum, final StringBuilder buffer, final Ide
120146 writeEscapedString (datum .toString (), buffer );
121147 buffer .append ("\" " );
122148 } else if (isBytes (datum )) {
123- final String bytesAsString = StandardCharsets .UTF_8 .decode ((ByteBuffer ) datum ).toString ();
124- final Optional <BigDecimal > bytesAsBigDecimal = getBigDecimal (bytesAsString );
125- if (bytesAsBigDecimal .isPresent ()) {
126- buffer .append (bytesAsBigDecimal .get ().doubleValue ());
149+ if (decimalScale != null ) {
150+ ByteBuffer sourceBuffer = (ByteBuffer ) datum ;
151+ byte [] bytesArray = new byte [sourceBuffer .remaining ()];
152+ sourceBuffer .duplicate ().get (bytesArray );
153+ BigInteger unscaledValue = new BigInteger (bytesArray );
154+ BigDecimal decimal = new BigDecimal (unscaledValue , decimalScale );
155+ buffer .append (decimal .doubleValue ());
127156 } else {
128- buffer .append ("{\" bytes\" : \" " );
129- ByteBuffer bytes = ((ByteBuffer ) datum ).duplicate ();
130- writeEscapedString (new String (bytes .array (), StandardCharsets .ISO_8859_1 ), buffer );
131- buffer .append ("\" }" );
157+ final String bytesAsString = StandardCharsets .UTF_8 .decode ((ByteBuffer ) datum ).toString ();
158+ final Optional <BigDecimal > bytesAsBigDecimal = getBigDecimal (bytesAsString );
159+ if (bytesAsBigDecimal .isPresent ()) {
160+ buffer .append (bytesAsBigDecimal .get ().doubleValue ());
161+ } else {
162+ buffer .append ("{\" bytes\" : \" " );
163+ ByteBuffer bytes = ((ByteBuffer ) datum ).duplicate ();
164+ writeEscapedString (new String (bytes .array (), StandardCharsets .ISO_8859_1 ), buffer );
165+ buffer .append ("\" }" );
166+ }
132167 }
133168 } else if (((datum instanceof Float ) && // quote Nan & Infinity
134169 (((Float )datum ).isInfinite () || ((Float )datum ).isNaN ()))
@@ -143,7 +178,7 @@ private void serialize(final Object datum, final StringBuilder buffer, final Ide
143178 return ;
144179 }
145180 seenObjects .put (datum , datum );
146- serialize (datum , buffer , seenObjects );
181+ serialize (datum , buffer , seenObjects , null );
147182 seenObjects .remove (datum );
148183 } else {
149184 // This fallback is the reason why GenericRecord toString does not
0 commit comments