001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.jsonpatch;
018
019import java.io.*;
020import java.lang.reflect.*;
021
022import org.apache.juneau.marshall.json.*;
023import org.apache.juneau.marshall.marshaller.*;
024import org.apache.juneau.marshall.parser.*;
025import org.apache.juneau.marshall.serializer.*;
026
027/**
028 * A pairing of a {@link JsonSerializer} and {@link JsonParser} into a single class with convenience to/of methods,
029 * pre-wired for JSON Patch's {@link JsonPatchOperation} discriminator.
030 *
031 * <p>
032 * Every {@link JsonSerializer}/{@link JsonParser} used to read or write {@link JsonPatchOperation} (or
033 * {@link JsonPatch}) needs to be configured with
034 * <c>.typePropertyName(JsonPatchOperation.<jk>class</jk>, <js>"op"</js>)</c> so that polymorphic dispatch across
035 * {@link AddOp}, {@link RemoveOp}, {@link ReplaceOp}, {@link MoveOp}, {@link CopyOp}, and {@link TestOp} resolves
036 * via the RFC 6902 {@code "op"} member rather than the default {@code "_type"} property. This class hand-wires
037 * that configuration once so callers don't have to repeat it.
038 *
039 * <h5 class='figure'>Examples:</h5>
040 * <p class='bjava'>
041 *    <jc>// Using instance.</jc>
042 *    JsonPatchMarshaller <jv>m</jv> = <jk>new</jk> JsonPatchMarshaller();
043 *    JsonPatch <jv>patch</jv> = <jv>m</jv>.read(<jv>string</jv>, JsonPatch.<jk>class</jk>);
044 *    String <jv>string</jv> = <jv>m</jv>.write(<jv>patch</jv>);
045 * </p>
046 * <p class='bjava'>
047 * <jc>// Using DEFAULT instance.</jc>
048 *    JsonPatch <jv>patch</jv> = JsonPatchMarshaller.<jsf>DEFAULT</jsf>.read(<jv>string</jv>, JsonPatch.<jk>class</jk>);
049 *    String <jv>string</jv> = JsonPatchMarshaller.<jsf>DEFAULT</jsf>.write(<jv>patch</jv>);
050 * </p>
051 * <p class='bjava'>
052 * <jc>// Using static shortcuts.</jc>
053 *    JsonPatch <jv>patch</jv> = JsonPatchMarshaller.<jsm>to</jsm>(<jv>string</jv>, JsonPatch.<jk>class</jk>);
054 *    String <jv>string</jv> = JsonPatchMarshaller.<jsm>of</jsm>(<jv>patch</jv>);
055 * </p>
056 *
057 * <h5 class='figure'>Example output:</h5>
058 * <p class='bjson'>
059 *    [{<js>"op"</js>:<js>"add"</js>,<js>"path"</js>:<js>"/a/b/c"</js>,<js>"value"</js>:<js>"foo"</js>},{<js>"op"</js>:<js>"remove"</js>,<js>"path"</js>:<js>"/a/b/c"</js>}]
060 * </p>
061 *
062 * <h5 class='section'>See Also:</h5><ul>
063 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshallers">Marshallers</a>
064 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanJsonPatch">juneau-bean-jsonpatch</a>
065 * </ul>
066 */
067public class JsonPatchMarshaller extends CharMarshaller {
068
069   /**
070    * Default {@link JsonSerializer}, pre-wired with the {@code "op"} discriminator for {@link JsonPatchOperation}.
071    */
072   public static final JsonSerializer SERIALIZER = JsonSerializer.create()
073      .addBeanTypes()
074      .addRootType()
075      .typePropertyName(JsonPatchOperation.class, "op")
076      .build();
077
078   /**
079    * Default {@link JsonParser}, pre-wired with the {@code "op"} discriminator for {@link JsonPatchOperation}.
080    */
081   public static final JsonParser PARSER = JsonParser.create()
082      .typePropertyName(JsonPatchOperation.class, "op")
083      .build();
084
085   /** Default marshaller instance. */
086   public static final JsonPatchMarshaller DEFAULT = new JsonPatchMarshaller();
087
088   /**
089    * Serializes a POJO to a <c>String</c> using the {@link #DEFAULT} marshaller.
090    *
091    * <p>
092    * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>)</c>.
093    *
094    * @param object The object to serialize.
095    * @return The serialized object.
096    * @throws SerializeException If a problem occurred trying to convert the output.
097    */
098   public static String of(Object object) throws SerializeException {
099      return DEFAULT.write(object);
100   }
101
102   /**
103    * Parses an input into the specified object type using the {@link #DEFAULT} marshaller.
104    *
105    * <p>
106    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c>.
107    *
108    * @param <T> The class type of the object being created.
109    * @param input The input.
110    * @param type The object type to create.
111    * @return The parsed object.
112    * @throws ParseException Malformed input encountered.
113    */
114   public static <T> T to(String input, Class<T> type) throws ParseException {
115      return DEFAULT.read(input, type);
116   }
117
118   /**
119    * Parses an input into the specified parameterized object type using the {@link #DEFAULT} marshaller.
120    *
121    * <p>
122    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c>.
123    *
124    * @param <T> The class type of the object to create.
125    * @param input The input.
126    * @param type The object type to create.
127    * @param args The type arguments of the class if it's a collection or map.
128    * @return The parsed object.
129    * @throws ParseException Malformed input encountered.
130    */
131   public static <T> T to(String input, Type type, Type... args) throws ParseException {
132      return DEFAULT.read(input, type, args);
133   }
134
135   /**
136    * Parses the contents of a {@link Reader} into the specified object type using the {@link #DEFAULT} marshaller.
137    *
138    * <p>
139    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c> that catches any
140    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the
141    * caller is not burdened with a checked exception.
142    *
143    * @param <T> The class type of the object being created.
144    * @param input The input reader.
145    * @param type The object type to create.
146    * @return The parsed object.
147    * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream.
148    */
149   public static <T> T to(Reader input, Class<T> type) throws ParseException {
150      try {
151         return DEFAULT.read(input, type);
152      } catch (IOException e) {
153         throw new ParseException(e);
154      }
155   }
156
157   /**
158    * Parses the contents of a {@link Reader} into the specified parameterized object type using the {@link #DEFAULT} marshaller.
159    *
160    * <p>
161    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c> that catches any
162    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the
163    * caller is not burdened with a checked exception.
164    *
165    * @param <T> The class type of the object to create.
166    * @param input The input reader.
167    * @param type The object type to create.
168    * @param args The type arguments of the class if it's a collection or map.
169    * @return The parsed object.
170    * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream.
171    */
172   public static <T> T to(Reader input, Type type, Type... args) throws ParseException {
173      try {
174         return DEFAULT.read(input, type, args);
175      } catch (IOException e) {
176         throw new ParseException(e);
177      }
178   }
179
180   /**
181    * Serializes a POJO to the specified {@link Writer} using the {@link #DEFAULT} marshaller.
182    *
183    * <p>
184    * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>, <jv>output</jv>)</c> that catches any
185    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link SerializeException}, so the
186    * caller is not burdened with a checked exception.
187    *
188    * @param object The object to serialize.
189    * @param output The writer to serialize to.
190    * @throws SerializeException If a problem occurred trying to convert the output or an I/O error occurred on the underlying stream.
191    */
192   public static void of(Object object, Writer output) throws SerializeException {
193      try {
194         DEFAULT.write(object, output);
195      } catch (IOException e) { // HTT: required by the checked IOException on Marshaller.write(); DEFAULT's serializer wraps every writer and pipe I/O failure in SerializeException or a runtime exception first, and a seam would make this testable
196         throw new SerializeException(e); // HTT: only reachable from the catch above
197      }
198   }
199
200   /** Creates using the pre-wired {@link #SERIALIZER} and {@link #PARSER}. */
201   public JsonPatchMarshaller() {
202      this(SERIALIZER, PARSER);
203   }
204
205   /**
206    * Creates with custom serializer and parser.
207    *
208    * <p>
209    * Callers overriding these should preserve the <c>.typePropertyName(JsonPatchOperation.class, "op")</c>
210    * configuration (and, on the serializer side, <c>.addBeanTypes()</c>/<c>.addRootType()</c>) or polymorphic
211    * dispatch across the {@link JsonPatchOperation} subclasses will not round-trip correctly.
212    *
213    * @param s The serializer.
214    * @param p The parser.
215    */
216   public JsonPatchMarshaller(JsonSerializer s, JsonParser p) {
217      super(s, p);
218   }
219}