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.jsonapi; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.util.*; 023 024import org.apache.juneau.commons.bean.*; 025import org.apache.juneau.marshall.*; 026import org.apache.juneau.marshall.marshaller.*; 027 028/** 029 * Represents a JSON:API Resource Object as defined by 030 * <a href="https://jsonapi.org/format/#document-resource-objects">JSON:API v1.1 § Resource Objects</a>. 031 * 032 * <p> 033 * Carries the standard six members: {@code type}, {@code id}, {@code attributes}, {@code relationships}, 034 * {@code links}, {@code meta}. 035 * 036 * <h5 class='topic'>Why {@code type} is a plain String</h5> 037 * 038 * <p> 039 * JSON:API uses {@code type} as an open-ended string field on resource objects (the entity-type name, e.g. 040 * {@code "articles"}); the value is chosen by the API and is <b>not</b> a closed Java class hierarchy. 041 * Juneau's {@code @Marshalled(typePropertyName=...)} discriminator defaults to {@code _type} (so there is also 042 * no literal name clash). <b>Do NOT</b> annotate {@code JsonApiResource} with 043 * {@code @Marshalled(typePropertyName="type", dictionary=...)} — that would conflate JSON:API resource typing 044 * with Juneau bean dispatch, break round-tripping of unknown {@code type} values, and force every API to 045 * enumerate its resource types in a Java dictionary up front. 046 * 047 * <h5 class='section'>See Also:</h5><ul> 048 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanJsonApi">juneau-bean-jsonapi</a> 049 * </ul> 050 */ 051@Marshalled 052public class JsonApiResource { 053 054 private String type; 055 private String id; 056 private Map<String,Object> attributes; 057 private Map<String,JsonApiRelationship> relationships; 058 private Map<String,Object> links; 059 private Map<String,Object> meta; 060 061 /** 062 * Default constructor. 063 */ 064 public JsonApiResource() {} 065 066 /** 067 * Convenience constructor. 068 * 069 * @param type The entity-type name. Can be <jk>null</jk> to leave the property unset. 070 * @param id The identifier value. Can be <jk>null</jk> to leave the property unset. 071 */ 072 public JsonApiResource(String type, String id) { 073 this.type = type; 074 this.id = id; 075 } 076 077 /** 078 * Bean property getter: <property>type</property>. 079 * 080 * <p> 081 * Plain {@code String} entity-type name. See class-level Javadoc for why this is not a Juneau polymorphic 082 * discriminator. 083 * 084 * @return The value of the <property>type</property> property, or <jk>null</jk> if it is not set. 085 */ 086 public String getType() { return type; } 087 088 /** 089 * Bean property setter: <property>type</property>. 090 * 091 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 092 * @return This object. 093 */ 094 public JsonApiResource setType(String value) { 095 type = value; 096 return this; 097 } 098 099 /** 100 * Bean property getter: <property>id</property>. 101 * 102 * @return The value of the <property>id</property> property, or <jk>null</jk> if it is not set. 103 */ 104 public String getId() { return id; } 105 106 /** 107 * Bean property setter: <property>id</property>. 108 * 109 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 110 * @return This object. 111 */ 112 public JsonApiResource setId(String value) { 113 id = value; 114 return this; 115 } 116 117 /** 118 * Bean property getter: <property>attributes</property>. 119 * 120 * <p> 121 * The spec lets {@code attributes} be any JSON object. Modeled as {@code Map<String,Object>} (a nested 122 * object on the wire — not flattened). 123 * 124 * @return The value of the <property>attributes</property> property, or <jk>null</jk> if it is not set. 125 */ 126 public Map<String,Object> getAttributes() { return u(attributes); } 127 128 /** 129 * Bean property setter: <property>attributes</property>. 130 * 131 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 132 * @return This object. 133 */ 134 public JsonApiResource setAttributes(Map<String,Object> value) { 135 attributes = value; 136 return this; 137 } 138 139 /** 140 * Convenience method to add a single attribute. 141 * 142 * @param name The attribute name. Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key). 143 * @param value The attribute value. Can be <jk>null</jk> (stored as <jk>null</jk>). 144 * @return This object. 145 */ 146 public JsonApiResource putAttribute(String name, Object value) { 147 if (attributes == null) 148 attributes = map(); 149 attributes.put(name, value); 150 return this; 151 } 152 153 /** 154 * Bean property getter: <property>relationships</property>. 155 * 156 * @return The value of the <property>relationships</property> property, or <jk>null</jk> if it is not set. 157 */ 158 public Map<String,JsonApiRelationship> getRelationships() { return u(relationships); } 159 160 /** 161 * Bean property setter: <property>relationships</property>. 162 * 163 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 164 * @return This object. 165 */ 166 public JsonApiResource setRelationships(Map<String,JsonApiRelationship> value) { 167 relationships = value; 168 return this; 169 } 170 171 /** 172 * Convenience method to add a single named relationship. 173 * 174 * @param name The relationship name. Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key). 175 * @param value The relationship object. Can be <jk>null</jk> (stored as <jk>null</jk>). 176 * @return This object. 177 */ 178 public JsonApiResource putRelationship(String name, JsonApiRelationship value) { 179 if (relationships == null) 180 relationships = map(); 181 relationships.put(name, value); 182 return this; 183 } 184 185 /** 186 * Bean property getter: <property>links</property>. 187 * 188 * <p> 189 * Per spec, each {@code links} value can be either a JSON string URL or a {@link JsonApiLink} object. 190 * 191 * @return The value of the <property>links</property> property, or <jk>null</jk> if it is not set. 192 */ 193 @BeanProp(value="links") 194 @Swap(JsonApiLinkOrStringSwap.class) 195 public Map<String,Object> getLinks() { return u(links); } 196 197 /** 198 * Bean property setter: <property>links</property>. 199 * 200 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 201 * @return This object. 202 */ 203 @BeanProp(value="links") 204 @Swap(JsonApiLinkOrStringSwap.class) 205 public JsonApiResource setLinks(Map<String,Object> value) { 206 links = value; 207 return this; 208 } 209 210 /** 211 * Convenience method to add a single link. 212 * 213 * @param name The link name. Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key). 214 * @param value The link value (a {@link String} URL or a {@link JsonApiLink} object). Can be <jk>null</jk> (stored as <jk>null</jk>). 215 * @return This object. 216 */ 217 public JsonApiResource putLink(String name, Object value) { 218 if (links == null) 219 links = map(); 220 links.put(name, value); 221 return this; 222 } 223 224 /** 225 * Bean property getter: <property>meta</property>. 226 * 227 * @return The value of the <property>meta</property> property, or <jk>null</jk> if it is not set. 228 */ 229 public Map<String,Object> getMeta() { return u(meta); } 230 231 /** 232 * Bean property setter: <property>meta</property>. 233 * 234 * @param value The new value for this property. Can be <jk>null</jk> to unset the property. 235 * @return This object. 236 */ 237 public JsonApiResource setMeta(Map<String,Object> value) { 238 meta = value; 239 return this; 240 } 241 242 /** 243 * Convenience method to add a single meta entry. 244 * 245 * @param name The meta key. Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key). 246 * @param value The meta value. Can be <jk>null</jk> (stored as <jk>null</jk>). 247 * @return This object. 248 */ 249 public JsonApiResource putMeta(String name, Object value) { 250 if (meta == null) 251 meta = map(); 252 meta.put(name, value); 253 return this; 254 } 255 256 @Override /* Overridden from Object */ 257 public String toString() { 258 return Json.of(this); 259 } 260}